Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract code to automatically derive data from maven-project #6362

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,165 @@ 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 = Optional.ofNullable(configuration.getChild("outputTimestamp"))
.map(xpp -> xpp.getValue())
.orElse(null);
// 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 +280,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()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this here needs to also look in the parentage for properties. The failure stems from not finding the output time stamp in the parent pom of the project. So it is configuration(parentProject)+configuration(thisProject). Also need to allow the descendant projects to override the ancestor projects.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think you are right here, I was hoping this is already resolved by maven but it seems not be the case...

}

private Optional<Xpp3Dom> getConfiguration(List<Plugin> plugins) {
return plugins.stream()
.filter(p -> Objects.equals(p, mojoExecution.getPlugin()))
Expand All @@ -126,4 +302,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
Loading