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