Skip to content

Commit f439ce8

Browse files
committed
Extract code to automatically derive data from maven-project
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]>
1 parent 0e76ec6 commit f439ce8

File tree

3 files changed

+201
-182
lines changed

3 files changed

+201
-182
lines changed

biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/BndConfiguration.java

+197
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,35 @@
77
import java.util.Objects;
88
import java.util.Optional;
99

10+
import org.apache.maven.model.Developer;
11+
import org.apache.maven.model.License;
1012
import org.apache.maven.model.Plugin;
1113
import org.apache.maven.model.PluginExecution;
1214
import org.apache.maven.model.PluginManagement;
1315
import org.apache.maven.plugin.MojoExecution;
1416
import org.apache.maven.project.MavenProject;
17+
import org.codehaus.plexus.util.StringUtils;
1518
import org.codehaus.plexus.util.xml.Xpp3Dom;
1619
import org.slf4j.Logger;
1720
import org.slf4j.LoggerFactory;
1821

1922
import aQute.bnd.build.Project;
23+
import aQute.bnd.header.OSGiHeader;
24+
import aQute.bnd.osgi.Constants;
2025
import aQute.bnd.osgi.Processor;
26+
import aQute.bnd.version.MavenVersion;
27+
import aQute.bnd.version.Version;
2128
import aQute.lib.io.IO;
29+
import aQute.lib.strings.Strings;
2230
import aQute.lib.utf8properties.UTF8Properties;
2331

2432
/**
2533
* A helper to read Bnd configuration for maven plugins consistently over the
2634
* various Mojos.
2735
*/
2836
public class BndConfiguration {
37+
static final String TSTAMP = "${tstamp}";
38+
static final String SNAPSHOT = "SNAPSHOT";
2939
private final static Logger logger = LoggerFactory.getLogger(BndConfiguration.class);
3040

3141
private final MavenProject project;
@@ -47,6 +57,166 @@ public File loadProperties(Processor processor) throws Exception {
4757
return loadProjectProperties(processor, project, project, configuration);
4858
}
4959

60+
public void inheritPropertiesDefaults(Processor processor) throws Exception {
61+
Xpp3Dom configuration = getConfiguration(project)
62+
.orElseGet(this::defaultConfiguration);
63+
// https://maven.apache.org/guides/mini/guide-reproducible-builds.html
64+
String outputTimestamp = Optional.ofNullable(configuration.getChild("outputTimestamp"))
65+
.map(xpp -> xpp.getValue())
66+
.orElse(project.getProperties()
67+
.getProperty("project.build.outputTimestamp"));
68+
boolean isReproducible = Strings.nonNullOrEmpty(outputTimestamp)
69+
// no timestamp configured (1 character configuration is useful
70+
// to override a full value during pom inheritance)
71+
&& ((outputTimestamp.length() > 1) || Character.isDigit(outputTimestamp.charAt(0)));
72+
if (isReproducible) {
73+
processor.setProperty(Constants.REPRODUCIBLE, outputTimestamp);
74+
if (processor.getProperty(Constants.NOEXTRAHEADERS) == null) {
75+
processor.setProperty(Constants.NOEXTRAHEADERS, Boolean.TRUE.toString());
76+
}
77+
}
78+
// Set Bundle-SymbolicName
79+
if (processor.getProperty(Constants.BUNDLE_SYMBOLICNAME) == null) {
80+
processor.setProperty(Constants.BUNDLE_SYMBOLICNAME, project.getArtifactId());
81+
}
82+
// Set Bundle-Name
83+
if (processor.getProperty(Constants.BUNDLE_NAME) == null) {
84+
processor.setProperty(Constants.BUNDLE_NAME, project.getName());
85+
}
86+
// Set Bundle-Version
87+
String snapshot = isReproducible ? SNAPSHOT : null;
88+
if (processor.getProperty(Constants.BUNDLE_VERSION) == null) {
89+
Version version = new MavenVersion(project.getVersion()).getOSGiVersion();
90+
processor.setProperty(Constants.BUNDLE_VERSION, version.toString());
91+
if (snapshot == null) {
92+
snapshot = TSTAMP;
93+
}
94+
}
95+
if (snapshot != null) {
96+
if (processor.getProperty(Constants.SNAPSHOT) == null) {
97+
processor.setProperty(Constants.SNAPSHOT, snapshot);
98+
}
99+
}
100+
101+
// Set Bundle-Description
102+
if (processor.getProperty(Constants.BUNDLE_DESCRIPTION) == null) {
103+
// may be null
104+
if (StringUtils.isNotBlank(project.getDescription())) {
105+
processor.setProperty(Constants.BUNDLE_DESCRIPTION, project.getDescription());
106+
}
107+
}
108+
109+
// Set Bundle-Vendor
110+
if (processor.getProperty(Constants.BUNDLE_VENDOR) == null) {
111+
if (project.getOrganization() != null && StringUtils.isNotBlank(project.getOrganization()
112+
.getName())) {
113+
processor.setProperty(Constants.BUNDLE_VENDOR, project.getOrganization()
114+
.getName());
115+
}
116+
}
117+
118+
// Set Bundle-License
119+
if (processor.getProperty(Constants.BUNDLE_LICENSE) == null) {
120+
StringBuilder licenses = new StringBuilder();
121+
for (License license : project.getLicenses()) {
122+
addHeaderValue(licenses, license.getName(), ',');
123+
// link is optional
124+
if (StringUtils.isNotBlank(license.getUrl())) {
125+
addHeaderAttribute(licenses, "link", license.getUrl(), ';');
126+
}
127+
// comment is optional
128+
if (StringUtils.isNotBlank(license.getComments())) {
129+
addHeaderAttribute(licenses, "description", license.getComments(), ';');
130+
}
131+
}
132+
if (licenses.length() > 0) {
133+
processor.setProperty(Constants.BUNDLE_LICENSE, licenses.toString());
134+
}
135+
}
136+
137+
// Set Bundle-SCM
138+
if (processor.getProperty(Constants.BUNDLE_SCM) == null) {
139+
StringBuilder scm = new StringBuilder();
140+
if (project.getScm() != null) {
141+
if (StringUtils.isNotBlank(project.getScm()
142+
.getUrl())) {
143+
addHeaderAttribute(scm, "url", project.getScm()
144+
.getUrl(), ',');
145+
}
146+
if (StringUtils.isNotBlank(project.getScm()
147+
.getConnection())) {
148+
addHeaderAttribute(scm, "connection", project.getScm()
149+
.getConnection(), ',');
150+
}
151+
if (StringUtils.isNotBlank(project.getScm()
152+
.getDeveloperConnection())) {
153+
addHeaderAttribute(scm, "developer-connection", project.getScm()
154+
.getDeveloperConnection(), ',');
155+
}
156+
if (StringUtils.isNotBlank(project.getScm()
157+
.getTag())) {
158+
addHeaderAttribute(scm, "tag", project.getScm()
159+
.getTag(), ',');
160+
}
161+
if (scm.length() > 0) {
162+
processor.setProperty(Constants.BUNDLE_SCM, scm.toString());
163+
}
164+
}
165+
}
166+
167+
// Set Bundle-Developers
168+
if (processor.getProperty(Constants.BUNDLE_DEVELOPERS) == null) {
169+
StringBuilder developers = new StringBuilder();
170+
// this is never null
171+
for (Developer developer : project.getDevelopers()) {
172+
// id is mandatory for OSGi but not enforced in the pom.xml
173+
if (StringUtils.isNotBlank(developer.getId())) {
174+
addHeaderValue(developers, developer.getId(), ',');
175+
// all attributes are optional
176+
if (StringUtils.isNotBlank(developer.getEmail())) {
177+
addHeaderAttribute(developers, "email", developer.getEmail(), ';');
178+
}
179+
if (StringUtils.isNotBlank(developer.getName())) {
180+
addHeaderAttribute(developers, "name", developer.getName(), ';');
181+
}
182+
if (StringUtils.isNotBlank(developer.getOrganization())) {
183+
addHeaderAttribute(developers, "organization", developer.getOrganization(), ';');
184+
}
185+
if (StringUtils.isNotBlank(developer.getOrganizationUrl())) {
186+
addHeaderAttribute(developers, "organizationUrl", developer.getOrganizationUrl(), ';');
187+
}
188+
if (!developer.getRoles()
189+
.isEmpty()) {
190+
addHeaderAttribute(developers, "roles", StringUtils.join(developer.getRoles()
191+
.iterator(), ","), ';');
192+
}
193+
if (StringUtils.isNotBlank(developer.getTimezone())) {
194+
addHeaderAttribute(developers, "timezone", developer.getTimezone(), ';');
195+
}
196+
} else {
197+
logger.warn(
198+
"Cannot consider developer in line '{}' of file '{}' for bundle header '{}' as it does not contain the mandatory id.",
199+
developer.getLocation("")
200+
.getLineNumber(),
201+
developer.getLocation("")
202+
.getSource()
203+
.getLocation(),
204+
Constants.BUNDLE_DEVELOPERS);
205+
}
206+
}
207+
if (developers.length() > 0) {
208+
processor.setProperty(Constants.BUNDLE_DEVELOPERS, developers.toString());
209+
}
210+
}
211+
212+
// Set Bundle-DocURL
213+
if (processor.getProperty(Constants.BUNDLE_DOCURL) == null) {
214+
if (StringUtils.isNotBlank(project.getUrl())) {
215+
processor.setProperty(Constants.BUNDLE_DOCURL, project.getUrl());
216+
}
217+
}
218+
}
219+
50220
private void loadParentProjectProperties(Processor builder, MavenProject currentProject) throws Exception {
51221
MavenProject parentProject = currentProject.getParent();
52222
if (parentProject == null) {
@@ -111,6 +281,13 @@ private File loadProjectProperties(Processor processor, MavenProject bndProject,
111281
return pomFile;
112282
}
113283

284+
private Optional<Xpp3Dom> getConfiguration(MavenProject mavenProject) {
285+
if (mavenProject == null) {
286+
return Optional.empty();
287+
}
288+
return getConfiguration(mavenProject.getBuildPlugins()).or(() -> getConfiguration(mavenProject.getParent()));
289+
}
290+
114291
private Optional<Xpp3Dom> getConfiguration(List<Plugin> plugins) {
115292
return plugins.stream()
116293
.filter(p -> Objects.equals(p, mojoExecution.getPlugin()))
@@ -126,4 +303,24 @@ private Optional<Xpp3Dom> getConfiguration(List<Plugin> plugins) {
126303
private Xpp3Dom defaultConfiguration() {
127304
return new Xpp3Dom("configuration");
128305
}
306+
307+
private static StringBuilder addHeaderValue(StringBuilder builder, String value, char separator) {
308+
if (builder.length() > 0) {
309+
builder.append(separator);
310+
}
311+
// use quoted string if necessary
312+
OSGiHeader.quote(builder, value);
313+
return builder;
314+
}
315+
316+
private static StringBuilder addHeaderAttribute(StringBuilder builder, String key, String value, char separator) {
317+
if (builder.length() > 0) {
318+
builder.append(separator);
319+
}
320+
builder.append(key)
321+
.append("=");
322+
// use quoted string if necessary
323+
OSGiHeader.quote(builder, value);
324+
return builder;
325+
}
129326
}

biz.aQute.bnd.maven/src/aQute/bnd/maven/lib/configuration/package-info.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@Version("1.2.0")
1+
@Version("1.3.0")
22
@Export
33
package aQute.bnd.maven.lib.configuration;
44

0 commit comments

Comments
 (0)