diff --git a/biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/BndConfiguration.java b/biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/BndConfiguration.java index 60253bcde5..7f2fa9db79 100644 --- a/biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/BndConfiguration.java +++ b/biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/BndConfiguration.java @@ -7,18 +7,26 @@ 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; /** @@ -26,6 +34,8 @@ * 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; @@ -47,6 +57,160 @@ public File loadProperties(Processor processor) throws Exception { return loadProjectProperties(processor, project, project, configuration); } + public void applyDefaults(Processor processor, String outputTimestamp) throws Exception { + // 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) { @@ -126,4 +290,24 @@ private Optional getConfiguration(List 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; + } } diff --git a/biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/package-info.java b/biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/package-info.java index 46493f165a..41d103135e 100644 --- a/biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/package-info.java +++ b/biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/package-info.java @@ -1,4 +1,4 @@ -@Version("1.2.0") +@Version("1.3.0") @Export package aQute.bnd.maven.lib.configuration; diff --git a/maven-plugins/bnd-maven-plugin/src/main/java/aQute/bnd/maven/plugin/AbstractBndMavenPlugin.java b/maven-plugins/bnd-maven-plugin/src/main/java/aQute/bnd/maven/plugin/AbstractBndMavenPlugin.java index b4388e474c..5dd2ab7b6d 100644 --- a/maven-plugins/bnd-maven-plugin/src/main/java/aQute/bnd/maven/plugin/AbstractBndMavenPlugin.java +++ b/maven-plugins/bnd-maven-plugin/src/main/java/aQute/bnd/maven/plugin/AbstractBndMavenPlugin.java @@ -40,27 +40,20 @@ import aQute.bnd.build.Project; import aQute.bnd.exceptions.Exceptions; -import aQute.bnd.header.OSGiHeader; import aQute.bnd.maven.PomPropertiesResource; import aQute.bnd.maven.lib.configuration.BeanProperties; import aQute.bnd.maven.lib.configuration.BndConfiguration; -import aQute.bnd.osgi.Builder; import aQute.bnd.osgi.Constants; import aQute.bnd.osgi.FileResource; import aQute.bnd.osgi.Jar; import aQute.bnd.osgi.Processor; import aQute.bnd.osgi.Resource; -import aQute.bnd.version.MavenVersion; -import aQute.bnd.version.Version; import aQute.lib.io.IO; import aQute.lib.strings.Strings; -import aQute.service.reporter.Report.Location; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.handler.ArtifactHandler; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; -import org.apache.maven.model.Developer; -import org.apache.maven.model.License; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecutionException; @@ -71,8 +64,6 @@ import org.apache.maven.project.MavenProjectHelper; import org.apache.maven.settings.Settings; import org.apache.maven.shared.mapping.MappingUtils; -import org.codehaus.plexus.util.StringUtils; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonatype.plexus.build.incremental.BuildContext; @@ -85,8 +76,6 @@ public abstract class AbstractBndMavenPlugin extends AbstractMojo { static final String MARKED_FILES = "aQute.bnd.maven.plugin.BndMavenPlugin.markedFiles"; static final String PACKAGING_JAR = "jar"; static final String PACKAGING_WAR = "war"; - static final String TSTAMP = "${tstamp}"; - static final String SNAPSHOT = "SNAPSHOT"; /** * Whether to include the contents of the {@code classesDir} directory @@ -245,7 +234,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { builder.setTrace(logger.isDebugEnabled()); builder.setBase(project.getBasedir()); - propertiesFile = new BndConfiguration(project, mojoExecution).loadProperties(builder); + BndConfiguration configuration = new BndConfiguration(project, mojoExecution); + propertiesFile = configuration.loadProperties(builder); builder.setProperty("project.output", getClassesDir().getCanonicalPath()); // If no bundle to be built, we have nothing to do @@ -374,158 +364,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { processBuilder(builder); - // 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) { - builder.setProperty(Constants.REPRODUCIBLE, outputTimestamp); - if (builder.getProperty(Constants.NOEXTRAHEADERS) == null) { - builder.setProperty(Constants.NOEXTRAHEADERS, Boolean.TRUE.toString()); - } - } - - // Set Bundle-SymbolicName - if (builder.getProperty(Constants.BUNDLE_SYMBOLICNAME) == null) { - builder.setProperty(Constants.BUNDLE_SYMBOLICNAME, project.getArtifactId()); - } - // Set Bundle-Name - if (builder.getProperty(Constants.BUNDLE_NAME) == null) { - builder.setProperty(Constants.BUNDLE_NAME, project.getName()); - } - // Set Bundle-Version - String snapshot = isReproducible ? SNAPSHOT : null; - if (builder.getProperty(Constants.BUNDLE_VERSION) == null) { - Version version = new MavenVersion(project.getVersion()).getOSGiVersion(); - builder.setProperty(Constants.BUNDLE_VERSION, version.toString()); - if (snapshot == null) { - snapshot = TSTAMP; - } - } - if (snapshot != null) { - if (builder.getProperty(Constants.SNAPSHOT) == null) { - builder.setProperty(Constants.SNAPSHOT, snapshot); - } - } - - // Set Bundle-Description - if (builder.getProperty(Constants.BUNDLE_DESCRIPTION) == null) { - // may be null - if (StringUtils.isNotBlank(project.getDescription())) { - builder.setProperty(Constants.BUNDLE_DESCRIPTION, project.getDescription()); - } - } - - // Set Bundle-Vendor - if (builder.getProperty(Constants.BUNDLE_VENDOR) == null) { - if (project.getOrganization() != null && StringUtils.isNotBlank(project.getOrganization() - .getName())) { - builder.setProperty(Constants.BUNDLE_VENDOR, project.getOrganization() - .getName()); - } - } - - // Set Bundle-License - if (builder.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) { - builder.setProperty(Constants.BUNDLE_LICENSE, licenses.toString()); - } - } - - // Set Bundle-SCM - if (builder.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) { - builder.setProperty(Constants.BUNDLE_SCM, scm.toString()); - } - } - } - - // Set Bundle-Developers - if (builder.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) { - builder.setProperty(Constants.BUNDLE_DEVELOPERS, developers.toString()); - } - } - - // Set Bundle-DocURL - if (builder.getProperty(Constants.BUNDLE_DOCURL) == null) { - if (StringUtils.isNotBlank(project.getUrl())) { - builder.setProperty(Constants.BUNDLE_DOCURL, project.getUrl()); - } - } + configuration.applyDefaults(builder, outputTimestamp); logger.debug("builder properties: {}", builder.getProperties()); logger.debug("builder delta: {}", delta); @@ -602,26 +441,6 @@ protected void processBuilder(Builder builder) throws MojoFailureException {} */ protected void processBuildPath(List buildpath) {} - 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; - } - private void attachArtifactToProject(Jar bndJar) throws Exception { File artifactFile = getArtifactFile(); if (outOfDate(artifactFile) || artifactFile.lastModified() < bndJar.lastModified()) {