Skip to content

Commit

Permalink
Extract code to automatically derive data from maven-project
Browse files Browse the repository at this point in the history
Currently if one loads the configuration with
aQute.bnd.maven.lib.configuration.BndConfiguration then only the
configuration itself is loaded, but actually some implicit configuration
later apply (e.g. from the maven project configuration)
that is currently only performed by the AbstractBndMavenPlugin.

This extracts this logic from the AbstractBndMavenPlugin into the
BndConfiguration class for the following benefits:

- making the AbstractBndMavenPlugin maintain less code in the execute
method
- allow other users of BndConfiguration to easily inherit common
defaults
- separation of concerns, easier to debug and understand

Signed-off-by: Christoph Läubrich <[email protected]>
  • Loading branch information
laeubi committed Dec 11, 2024
1 parent d0279b9 commit e2e410b
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,35 @@
import java.util.Objects;
import java.util.Optional;

import org.apache.maven.model.Developer;
import org.apache.maven.model.License;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import aQute.bnd.build.Project;
import aQute.bnd.header.OSGiHeader;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Processor;
import aQute.bnd.version.MavenVersion;
import aQute.bnd.version.Version;
import aQute.lib.io.IO;
import aQute.lib.strings.Strings;
import aQute.lib.utf8properties.UTF8Properties;

/**
* A helper to read Bnd configuration for maven plugins consistently over the
* various Mojos.
*/
public class BndConfiguration {
static final String TSTAMP = "${tstamp}";
static final String SNAPSHOT = "SNAPSHOT";
private final static Logger logger = LoggerFactory.getLogger(BndConfiguration.class);

private final MavenProject project;
Expand All @@ -47,6 +57,163 @@ public File loadProperties(Processor processor) throws Exception {
return loadProjectProperties(processor, project, project, configuration);
}

public void inheritPropertiesDefaults(Processor processor) throws Exception {
Xpp3Dom configuration = getConfiguration(project)
.orElseGet(this::defaultConfiguration);
String outputTimestamp = configuration.getAttribute("outputTimestamp");
// https://maven.apache.org/guides/mini/guide-reproducible-builds.html
boolean isReproducible = Strings.nonNullOrEmpty(outputTimestamp)
// no timestamp configured (1 character configuration is useful
// to override a full value during pom inheritance)
&& ((outputTimestamp.length() > 1) || Character.isDigit(outputTimestamp.charAt(0)));
if (isReproducible) {
processor.setProperty(Constants.REPRODUCIBLE, outputTimestamp);
if (processor.getProperty(Constants.NOEXTRAHEADERS) == null) {
processor.setProperty(Constants.NOEXTRAHEADERS, Boolean.TRUE.toString());
}
}
// Set Bundle-SymbolicName
if (processor.getProperty(Constants.BUNDLE_SYMBOLICNAME) == null) {
processor.setProperty(Constants.BUNDLE_SYMBOLICNAME, project.getArtifactId());
}
// Set Bundle-Name
if (processor.getProperty(Constants.BUNDLE_NAME) == null) {
processor.setProperty(Constants.BUNDLE_NAME, project.getName());
}
// Set Bundle-Version
String snapshot = isReproducible ? SNAPSHOT : null;
if (processor.getProperty(Constants.BUNDLE_VERSION) == null) {
Version version = new MavenVersion(project.getVersion()).getOSGiVersion();
processor.setProperty(Constants.BUNDLE_VERSION, version.toString());
if (snapshot == null) {
snapshot = TSTAMP;
}
}
if (snapshot != null) {
if (processor.getProperty(Constants.SNAPSHOT) == null) {
processor.setProperty(Constants.SNAPSHOT, snapshot);
}
}

// Set Bundle-Description
if (processor.getProperty(Constants.BUNDLE_DESCRIPTION) == null) {
// may be null
if (StringUtils.isNotBlank(project.getDescription())) {
processor.setProperty(Constants.BUNDLE_DESCRIPTION, project.getDescription());
}
}

// Set Bundle-Vendor
if (processor.getProperty(Constants.BUNDLE_VENDOR) == null) {
if (project.getOrganization() != null && StringUtils.isNotBlank(project.getOrganization()
.getName())) {
processor.setProperty(Constants.BUNDLE_VENDOR, project.getOrganization()
.getName());
}
}

// Set Bundle-License
if (processor.getProperty(Constants.BUNDLE_LICENSE) == null) {
StringBuilder licenses = new StringBuilder();
for (License license : project.getLicenses()) {
addHeaderValue(licenses, license.getName(), ',');
// link is optional
if (StringUtils.isNotBlank(license.getUrl())) {
addHeaderAttribute(licenses, "link", license.getUrl(), ';');
}
// comment is optional
if (StringUtils.isNotBlank(license.getComments())) {
addHeaderAttribute(licenses, "description", license.getComments(), ';');
}
}
if (licenses.length() > 0) {
processor.setProperty(Constants.BUNDLE_LICENSE, licenses.toString());
}
}

// Set Bundle-SCM
if (processor.getProperty(Constants.BUNDLE_SCM) == null) {
StringBuilder scm = new StringBuilder();
if (project.getScm() != null) {
if (StringUtils.isNotBlank(project.getScm()
.getUrl())) {
addHeaderAttribute(scm, "url", project.getScm()
.getUrl(), ',');
}
if (StringUtils.isNotBlank(project.getScm()
.getConnection())) {
addHeaderAttribute(scm, "connection", project.getScm()
.getConnection(), ',');
}
if (StringUtils.isNotBlank(project.getScm()
.getDeveloperConnection())) {
addHeaderAttribute(scm, "developer-connection", project.getScm()
.getDeveloperConnection(), ',');
}
if (StringUtils.isNotBlank(project.getScm()
.getTag())) {
addHeaderAttribute(scm, "tag", project.getScm()
.getTag(), ',');
}
if (scm.length() > 0) {
processor.setProperty(Constants.BUNDLE_SCM, scm.toString());
}
}
}

// Set Bundle-Developers
if (processor.getProperty(Constants.BUNDLE_DEVELOPERS) == null) {
StringBuilder developers = new StringBuilder();
// this is never null
for (Developer developer : project.getDevelopers()) {
// id is mandatory for OSGi but not enforced in the pom.xml
if (StringUtils.isNotBlank(developer.getId())) {
addHeaderValue(developers, developer.getId(), ',');
// all attributes are optional
if (StringUtils.isNotBlank(developer.getEmail())) {
addHeaderAttribute(developers, "email", developer.getEmail(), ';');
}
if (StringUtils.isNotBlank(developer.getName())) {
addHeaderAttribute(developers, "name", developer.getName(), ';');
}
if (StringUtils.isNotBlank(developer.getOrganization())) {
addHeaderAttribute(developers, "organization", developer.getOrganization(), ';');
}
if (StringUtils.isNotBlank(developer.getOrganizationUrl())) {
addHeaderAttribute(developers, "organizationUrl", developer.getOrganizationUrl(), ';');
}
if (!developer.getRoles()
.isEmpty()) {
addHeaderAttribute(developers, "roles", StringUtils.join(developer.getRoles()
.iterator(), ","), ';');
}
if (StringUtils.isNotBlank(developer.getTimezone())) {
addHeaderAttribute(developers, "timezone", developer.getTimezone(), ';');
}
} else {
logger.warn(
"Cannot consider developer in line '{}' of file '{}' for bundle header '{}' as it does not contain the mandatory id.",
developer.getLocation("")
.getLineNumber(),
developer.getLocation("")
.getSource()
.getLocation(),
Constants.BUNDLE_DEVELOPERS);
}
}
if (developers.length() > 0) {
processor.setProperty(Constants.BUNDLE_DEVELOPERS, developers.toString());
}
}

// Set Bundle-DocURL
if (processor.getProperty(Constants.BUNDLE_DOCURL) == null) {
if (StringUtils.isNotBlank(project.getUrl())) {
processor.setProperty(Constants.BUNDLE_DOCURL, project.getUrl());
}
}
}

private void loadParentProjectProperties(Processor builder, MavenProject currentProject) throws Exception {
MavenProject parentProject = currentProject.getParent();
if (parentProject == null) {
Expand Down Expand Up @@ -111,6 +278,13 @@ private File loadProjectProperties(Processor processor, MavenProject bndProject,
return pomFile;
}

private Optional<Xpp3Dom> getConfiguration(MavenProject mavenProject) {
if (mavenProject == null) {
return Optional.empty();
}
return getConfiguration(mavenProject.getBuildPlugins()).or(() -> getConfiguration(mavenProject.getParent()));
}

private Optional<Xpp3Dom> getConfiguration(List<Plugin> plugins) {
return plugins.stream()
.filter(p -> Objects.equals(p, mojoExecution.getPlugin()))
Expand All @@ -126,4 +300,24 @@ private Optional<Xpp3Dom> getConfiguration(List<Plugin> plugins) {
private Xpp3Dom defaultConfiguration() {
return new Xpp3Dom("configuration");
}

private static StringBuilder addHeaderValue(StringBuilder builder, String value, char separator) {
if (builder.length() > 0) {
builder.append(separator);
}
// use quoted string if necessary
OSGiHeader.quote(builder, value);
return builder;
}

private static StringBuilder addHeaderAttribute(StringBuilder builder, String key, String value, char separator) {
if (builder.length() > 0) {
builder.append(separator);
}
builder.append(key)
.append("=");
// use quoted string if necessary
OSGiHeader.quote(builder, value);
return builder;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@Version("1.2.0")
@Version("1.3.0")
@Export
package aQute.bnd.maven.lib.configuration;

Expand Down
Loading

0 comments on commit e2e410b

Please sign in to comment.