7
7
import java .util .Objects ;
8
8
import java .util .Optional ;
9
9
10
+ import org .apache .maven .model .Developer ;
11
+ import org .apache .maven .model .License ;
10
12
import org .apache .maven .model .Plugin ;
11
13
import org .apache .maven .model .PluginExecution ;
12
14
import org .apache .maven .model .PluginManagement ;
13
15
import org .apache .maven .plugin .MojoExecution ;
14
16
import org .apache .maven .project .MavenProject ;
17
+ import org .codehaus .plexus .util .StringUtils ;
15
18
import org .codehaus .plexus .util .xml .Xpp3Dom ;
16
19
import org .slf4j .Logger ;
17
20
import org .slf4j .LoggerFactory ;
18
21
19
22
import aQute .bnd .build .Project ;
23
+ import aQute .bnd .header .OSGiHeader ;
24
+ import aQute .bnd .osgi .Constants ;
20
25
import aQute .bnd .osgi .Processor ;
26
+ import aQute .bnd .version .MavenVersion ;
27
+ import aQute .bnd .version .Version ;
21
28
import aQute .lib .io .IO ;
29
+ import aQute .lib .strings .Strings ;
22
30
import aQute .lib .utf8properties .UTF8Properties ;
23
31
24
32
/**
25
33
* A helper to read Bnd configuration for maven plugins consistently over the
26
34
* various Mojos.
27
35
*/
28
36
public class BndConfiguration {
37
+ static final String TSTAMP = "${tstamp}" ;
38
+ static final String SNAPSHOT = "SNAPSHOT" ;
29
39
private final static Logger logger = LoggerFactory .getLogger (BndConfiguration .class );
30
40
31
41
private final MavenProject project ;
@@ -47,6 +57,166 @@ public File loadProperties(Processor processor) throws Exception {
47
57
return loadProjectProperties (processor , project , project , configuration );
48
58
}
49
59
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
+
50
220
private void loadParentProjectProperties (Processor builder , MavenProject currentProject ) throws Exception {
51
221
MavenProject parentProject = currentProject .getParent ();
52
222
if (parentProject == null ) {
@@ -111,6 +281,13 @@ private File loadProjectProperties(Processor processor, MavenProject bndProject,
111
281
return pomFile ;
112
282
}
113
283
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
+
114
291
private Optional <Xpp3Dom > getConfiguration (List <Plugin > plugins ) {
115
292
return plugins .stream ()
116
293
.filter (p -> Objects .equals (p , mojoExecution .getPlugin ()))
@@ -126,4 +303,24 @@ private Optional<Xpp3Dom> getConfiguration(List<Plugin> plugins) {
126
303
private Xpp3Dom defaultConfiguration () {
127
304
return new Xpp3Dom ("configuration" );
128
305
}
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
+ }
129
326
}
0 commit comments