1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.felix.bundleplugin;
20
21
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.util.Iterator;
30 import java.util.LinkedHashMap;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 import java.util.Properties;
34 import java.util.jar.Manifest;
35
36 import aQute.bnd.header.Parameters;
37 import aQute.bnd.osgi.Instructions;
38 import aQute.bnd.osgi.Processor;
39 import aQute.lib.collections.ExtList;
40 import org.apache.maven.plugin.MojoExecutionException;
41 import org.apache.maven.plugin.MojoFailureException;
42 import org.apache.maven.plugins.annotations.LifecyclePhase;
43 import org.apache.maven.plugins.annotations.Mojo;
44 import org.apache.maven.plugins.annotations.Parameter;
45 import org.apache.maven.plugins.annotations.ResolutionScope;
46 import org.apache.maven.project.MavenProject;
47
48 import aQute.bnd.osgi.Analyzer;
49 import aQute.bnd.osgi.Builder;
50 import aQute.bnd.osgi.Jar;
51 import aQute.bnd.osgi.Resource;
52 import org.apache.maven.shared.dependency.graph.DependencyNode;
53
54
55
56
57
58 @Mojo( name = "manifest", requiresDependencyResolution = ResolutionScope.TEST,
59 threadSafe = true,
60 defaultPhase = LifecyclePhase.PROCESS_CLASSES)
61 public class ManifestPlugin extends BundlePlugin
62 {
63
64
65
66 @Parameter( property = "rebuildBundle" )
67 protected boolean rebuildBundle;
68
69
70
71
72
73
74 protected File scrLocation;
75
76
77
78
79
80 protected boolean exportScr;
81
82 @Override
83 protected void execute( MavenProject project, DependencyNode dependencyGraph, Map<String, String> instructions, Properties properties, Jar[] classpath )
84 throws MojoExecutionException
85 {
86 Analyzer analyzer;
87 try
88 {
89 analyzer = getAnalyzer(project, dependencyGraph, instructions, properties, classpath);
90 }
91 catch ( FileNotFoundException e )
92 {
93 throw new MojoExecutionException( "Cannot find " + e.getMessage()
94 + " (manifest goal must be run after compile phase)", e );
95 }
96 catch ( IOException e )
97 {
98 throw new MojoExecutionException( "Error trying to generate Manifest", e );
99 }
100 catch ( MojoFailureException e )
101 {
102 getLog().error( e.getLocalizedMessage() );
103 throw new MojoExecutionException( "Error(s) found in manifest configuration", e );
104 }
105 catch ( Exception e )
106 {
107 getLog().error( "An internal error occurred", e );
108 throw new MojoExecutionException( "Internal error in maven-bundle-plugin", e );
109 }
110
111 File outputFile = new File( manifestLocation, "MANIFEST.MF" );
112
113 try
114 {
115 writeManifest( analyzer, outputFile, niceManifest );
116 }
117 catch ( Exception e )
118 {
119 throw new MojoExecutionException( "Error trying to write Manifest to file " + outputFile, e );
120 }
121 finally
122 {
123 try
124 {
125 analyzer.close();
126 }
127 catch ( IOException e )
128 {
129 throw new MojoExecutionException( "Error trying to write Manifest to file " + outputFile, e );
130 }
131 }
132 }
133
134
135 public Manifest getManifest( MavenProject project, DependencyNode dependencyGraph, Jar[] classpath ) throws IOException, MojoFailureException,
136 MojoExecutionException, Exception
137 {
138 return getManifest( project, dependencyGraph, new LinkedHashMap<String, String>(), new Properties(), classpath );
139 }
140
141
142 public Manifest getManifest( MavenProject project, DependencyNode dependencyGraph, Map<String, String> instructions, Properties properties, Jar[] classpath )
143 throws IOException, MojoFailureException, MojoExecutionException, Exception
144 {
145 Analyzer analyzer = getAnalyzer(project, dependencyGraph, instructions, properties, classpath);
146
147 Jar jar = analyzer.getJar();
148 Manifest manifest = jar.getManifest();
149
150 if (exportScr)
151 {
152 scrLocation.mkdirs();
153
154 String bpHeader = analyzer.getProperty(Analyzer.SERVICE_COMPONENT);
155 Parameters map = Processor.parseHeader(bpHeader, null);
156 for (String root : map.keySet())
157 {
158 Map<String, Resource> dir = jar.getDirectories().get(root);
159 File location = new File(scrLocation, root);
160 if (dir == null || dir.isEmpty())
161 {
162 Resource resource = jar.getResource(root);
163 if (resource != null)
164 {
165 writeSCR(resource, location);
166 }
167 }
168 else
169 {
170 for (Map.Entry<String, Resource> entry : dir.entrySet())
171 {
172 String path = entry.getKey();
173 Resource resource = entry.getValue();
174 writeSCR(resource, new File(location, path));
175 }
176 }
177 }
178 }
179
180
181 analyzer.close();
182
183 return manifest;
184 }
185
186 protected void writeSCR(Resource resource, File destination) throws Exception
187 {
188 destination.getParentFile().mkdirs();
189 OutputStream os = new FileOutputStream(destination);
190 try
191 {
192 resource.write(os);
193 }
194 finally
195 {
196 os.close();
197 }
198 }
199
200 protected Analyzer getAnalyzer( MavenProject project, DependencyNode dependencyGraph, Jar[] classpath ) throws IOException, MojoExecutionException,
201 Exception
202 {
203 return getAnalyzer( project, dependencyGraph, new LinkedHashMap<String, String>(), new Properties(), classpath );
204 }
205
206
207 protected Analyzer getAnalyzer( MavenProject project, DependencyNode dependencyGraph, Map<String, String> instructions, Properties properties, Jar[] classpath )
208 throws IOException, MojoExecutionException, Exception
209 {
210 if ( rebuildBundle && supportedProjectTypes.contains( project.getArtifact().getType() ) )
211 {
212 return buildOSGiBundle( project, dependencyGraph, instructions, properties, classpath );
213 }
214
215 File file = getOutputDirectory();
216 if ( file == null )
217 {
218 file = project.getArtifact().getFile();
219 }
220
221 if ( !file.exists() )
222 {
223 if ( file.equals( getOutputDirectory() ) )
224 {
225 file.mkdirs();
226 }
227 else
228 {
229 throw new FileNotFoundException( file.getPath() );
230 }
231 }
232
233 Builder analyzer = getOSGiBuilder( project, instructions, properties, classpath );
234
235 analyzer.setJar( file );
236
237
238
239
240
241 boolean isOutputDirectory = file.equals( getOutputDirectory() );
242
243 if ( analyzer.getProperty( Analyzer.EXPORT_PACKAGE ) == null
244 && analyzer.getProperty( Analyzer.EXPORT_CONTENTS ) == null
245 && analyzer.getProperty( Analyzer.PRIVATE_PACKAGE ) == null && !isOutputDirectory )
246 {
247 String export = calculateExportsFromContents( analyzer.getJar() );
248 analyzer.setProperty( Analyzer.EXPORT_PACKAGE, export );
249 }
250
251 addMavenInstructions( project, dependencyGraph, analyzer );
252
253
254 if ( analyzer.getProperty( DependencyEmbedder.EMBED_DEPENDENCY ) != null && isOutputDirectory )
255 {
256 analyzer.build();
257 }
258 else
259 {
260 analyzer.mergeManifest( analyzer.getJar().getManifest() );
261 analyzer.getJar().setManifest( analyzer.calcManifest() );
262 }
263
264 mergeMavenManifest( project, dependencyGraph, analyzer );
265
266 boolean hasErrors = reportErrors( "Manifest " + project.getArtifact(), analyzer );
267 if ( hasErrors )
268 {
269 String failok = analyzer.getProperty( "-failok" );
270 if ( null == failok || "false".equalsIgnoreCase( failok ) )
271 {
272 throw new MojoFailureException( "Error(s) found in manifest configuration" );
273 }
274 }
275
276 Jar jar = analyzer.getJar();
277
278 if ( unpackBundle )
279 {
280 File outputFile = getOutputDirectory();
281 for ( Entry<String, Resource> entry : jar.getResources().entrySet() )
282 {
283 File entryFile = new File( outputFile, entry.getKey() );
284 if ( !entryFile.exists() || entry.getValue().lastModified() == 0 )
285 {
286 entryFile.getParentFile().mkdirs();
287 OutputStream os = new FileOutputStream( entryFile );
288 entry.getValue().write( os );
289 os.close();
290 }
291 }
292 }
293
294 return analyzer;
295 }
296
297
298 public static void writeManifest( Analyzer analyzer, File outputFile, boolean niceManifest ) throws Exception
299 {
300 Properties properties = analyzer.getProperties();
301 Manifest manifest = analyzer.getJar().getManifest();
302 if ( outputFile.exists() && properties.containsKey( "Merge-Headers" ) )
303 {
304 Manifest analyzerManifest = manifest;
305 manifest = new Manifest();
306 InputStream inputStream = new FileInputStream( outputFile );
307 try
308 {
309 manifest.read( inputStream );
310 }
311 finally
312 {
313 inputStream.close();
314 }
315 Instructions instructions = new Instructions( ExtList.from( analyzer.getProperty("Merge-Headers") ) );
316 mergeManifest( instructions, manifest, analyzerManifest );
317 }
318 else
319 {
320 File parentFile = outputFile.getParentFile();
321 parentFile.mkdirs();
322 }
323 writeManifest( manifest, outputFile, niceManifest );
324 }
325
326
327 public static void writeManifest( Manifest manifest, File outputFile, boolean niceManifest ) throws IOException
328 {
329 outputFile.getParentFile().mkdirs();
330
331 FileOutputStream os;
332 os = new FileOutputStream( outputFile );
333 try
334 {
335 ManifestWriter.outputManifest( manifest, os, niceManifest );
336 }
337 finally
338 {
339 try
340 {
341 os.close();
342 }
343 catch ( IOException e )
344 {
345
346 }
347 }
348 }
349
350
351
352
353
354 public static String calculateExportsFromContents( Jar bundle )
355 {
356 String ddel = "";
357 StringBuffer sb = new StringBuffer();
358 Map<String, Map<String, Resource>> map = bundle.getDirectories();
359 for ( Iterator<Entry<String, Map<String, Resource>>> i = map.entrySet().iterator(); i.hasNext(); )
360 {
361
362
363
364 Entry<String, Map<String, Resource>> entry = i.next();
365 if ( entry.getValue() == null || entry.getValue().isEmpty() )
366 continue;
367
368 String directory = entry.getKey();
369 if ( directory.equals( "META-INF" ) || directory.startsWith( "META-INF/" ) )
370 continue;
371 if ( directory.equals( "OSGI-OPT" ) || directory.startsWith( "OSGI-OPT/" ) )
372 continue;
373 if ( directory.equals( "/" ) )
374 continue;
375
376 if ( directory.endsWith( "/" ) )
377 directory = directory.substring( 0, directory.length() - 1 );
378
379 directory = directory.replace( '/', '.' );
380 sb.append( ddel );
381 sb.append( directory );
382 ddel = ",";
383 }
384 return sb.toString();
385 }
386 }