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