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