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.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.Writer;
29 import java.lang.reflect.Array;
30 import java.lang.reflect.Method;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.Enumeration;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.Iterator;
39 import java.util.LinkedHashMap;
40 import java.util.LinkedHashSet;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Properties;
44 import java.util.Set;
45 import java.util.TreeMap;
46 import java.util.jar.Attributes;
47 import java.util.jar.Manifest;
48
49 import org.apache.felix.bundleplugin.pom.PomWriter;
50 import org.apache.maven.archiver.ManifestSection;
51 import org.apache.maven.archiver.MavenArchiveConfiguration;
52 import org.apache.maven.archiver.MavenArchiver;
53 import org.apache.maven.artifact.Artifact;
54 import org.apache.maven.artifact.factory.ArtifactFactory;
55 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
56 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
57 import org.apache.maven.artifact.repository.ArtifactRepository;
58 import org.apache.maven.artifact.resolver.ArtifactCollector;
59 import org.apache.maven.artifact.resolver.ArtifactResolver;
60 import org.apache.maven.execution.MavenSession;
61 import org.apache.maven.model.Dependency;
62 import org.apache.maven.model.Exclusion;
63 import org.apache.maven.model.License;
64 import org.apache.maven.model.Model;
65 import org.apache.maven.model.Resource;
66 import org.apache.maven.plugin.AbstractMojo;
67 import org.apache.maven.plugin.MojoExecutionException;
68 import org.apache.maven.plugin.MojoFailureException;
69 import org.apache.maven.plugin.logging.Log;
70 import org.apache.maven.plugins.annotations.Component;
71 import org.apache.maven.plugins.annotations.LifecyclePhase;
72 import org.apache.maven.plugins.annotations.Mojo;
73 import org.apache.maven.plugins.annotations.Parameter;
74 import org.apache.maven.plugins.annotations.ResolutionScope;
75 import org.apache.maven.project.MavenProject;
76 import org.apache.maven.project.MavenProjectBuilder;
77 import org.apache.maven.project.MavenProjectHelper;
78 import org.apache.maven.project.ProjectBuildingException;
79 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
80 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
81 import org.apache.maven.shared.dependency.graph.DependencyNode;
82 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
83 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
84 import org.apache.maven.shared.osgi.DefaultMaven2OsgiConverter;
85 import org.apache.maven.shared.osgi.Maven2OsgiConverter;
86 import org.codehaus.plexus.archiver.UnArchiver;
87 import org.codehaus.plexus.archiver.manager.ArchiverManager;
88 import org.codehaus.plexus.util.DirectoryScanner;
89 import org.codehaus.plexus.util.FileUtils;
90 import org.codehaus.plexus.util.PropertyUtils;
91 import org.codehaus.plexus.util.StringUtils;
92 import org.codehaus.plexus.util.WriterFactory;
93
94 import aQute.bnd.header.Attrs;
95 import aQute.bnd.header.OSGiHeader;
96 import aQute.bnd.header.Parameters;
97 import aQute.bnd.osgi.Analyzer;
98 import aQute.bnd.osgi.Builder;
99 import aQute.bnd.osgi.Constants;
100 import aQute.bnd.osgi.Descriptors.PackageRef;
101 import aQute.bnd.osgi.EmbeddedResource;
102 import aQute.bnd.osgi.FileResource;
103 import aQute.bnd.osgi.Instruction;
104 import aQute.bnd.osgi.Instructions;
105 import aQute.bnd.osgi.Jar;
106 import aQute.bnd.osgi.Packages;
107 import aQute.bnd.osgi.Processor;
108 import aQute.lib.collections.ExtList;
109 import aQute.lib.spring.SpringXMLType;
110 import aQute.libg.generics.Create;
111
112
113
114
115
116
117 @Mojo( name = "bundle", requiresDependencyResolution = ResolutionScope.TEST,
118 threadSafe = true,
119 defaultPhase = LifecyclePhase.PACKAGE )
120 public class BundlePlugin extends AbstractMojo
121 {
122
123
124
125 @Parameter( property = "manifestLocation", defaultValue = "${project.build.outputDirectory}/META-INF" )
126 protected File manifestLocation;
127
128
129
130
131 @Parameter( property = "niceManifest", defaultValue = "false" )
132 protected boolean niceManifest;
133
134
135
136
137 @Parameter( property = "dumpInstructions" )
138 protected File dumpInstructions;
139
140
141
142
143 @Parameter( property = "dumpClasspath" )
144 protected File dumpClasspath;
145
146
147
148
149 @Parameter( property = "unpackBundle" )
150 protected boolean unpackBundle;
151
152
153
154
155 @Parameter( property = "excludeDependencies" )
156 protected String excludeDependencies;
157
158
159
160
161 @Parameter( defaultValue = "${project.build.finalName}")
162 private String finalName;
163
164
165
166
167
168 @Parameter
169 protected String classifier;
170
171
172
173
174
175 @Parameter
176 protected String packaging;
177
178
179
180
181 @Parameter
182 protected boolean createDependencyReducedPom;
183
184
185
186
187
188
189 @Parameter( defaultValue = "${basedir}/dependency-reduced-pom.xml" )
190 protected File dependencyReducedPomLocation;
191
192 @Component
193 private MavenProjectHelper m_projectHelper;
194
195 @Component
196 private ArchiverManager m_archiverManager;
197
198 @Component
199 private ArtifactHandlerManager m_artifactHandlerManager;
200
201 @Component
202 protected DependencyGraphBuilder m_dependencyGraphBuilder;
203
204
205 @Parameter( defaultValue = "${session}", readonly = true )
206 protected MavenSession session;
207
208
209
210
211
212 @Component
213 protected MavenProjectBuilder mavenProjectBuilder;
214
215 @Component
216 private DependencyTreeBuilder dependencyTreeBuilder;
217
218
219
220
221 @Component
222 protected DependencyGraphBuilder dependencyGraphBuilder;
223
224 @Component
225 private ArtifactMetadataSource artifactMetadataSource;
226
227 @Component
228 private ArtifactCollector artifactCollector;
229
230 @Component
231 protected ArtifactFactory artifactFactory;
232
233
234
235
236
237
238
239
240 protected ArtifactResolver artifactResolver;
241
242
243
244
245
246 @Parameter( readonly = true, required = true, defaultValue = "${localRepository}" )
247 protected ArtifactRepository localRepository;
248
249
250
251
252 @Parameter( readonly = true, required = true, defaultValue = "${project.remoteArtifactRepositories}" )
253 protected List<ArtifactRepository> remoteArtifactRepositories;
254
255
256
257
258
259
260 @Parameter
261 protected List<String> supportedProjectTypes = Arrays.asList("jar", "bundle");
262
263
264
265
266 @Parameter( defaultValue = "${project.build.outputDirectory}" )
267 private File outputDirectory;
268
269
270
271
272 @Parameter( defaultValue = "${project.build.directory}" )
273 private String buildDirectory;
274
275
276
277
278 @Parameter( defaultValue = "${project}", readonly = true, required = true )
279 private MavenProject project;
280
281
282
283
284 @Parameter
285 private Map<String, String> instructions = new LinkedHashMap<String, String>();
286
287
288
289
290 private final Maven2OsgiConverter m_maven2OsgiConverter = new DefaultMaven2OsgiConverter();
291
292
293
294
295 @Parameter
296 private MavenArchiveConfiguration archive;
297
298 @Parameter( defaultValue = "${session}", readonly = true, required = true )
299 private MavenSession m_mavenSession;
300
301 private static final String MAVEN_SYMBOLICNAME = "maven-symbolicname";
302 private static final String MAVEN_RESOURCES = "{maven-resources}";
303 private static final String MAVEN_TEST_RESOURCES = "{maven-test-resources}";
304 private static final String LOCAL_PACKAGES = "{local-packages}";
305 private static final String MAVEN_SOURCES = "{maven-sources}";
306 private static final String MAVEN_TEST_SOURCES = "{maven-test-sources}";
307 private static final String BUNDLE_PLUGIN_EXTENSION = "BNDExtension-";
308 private static final String BUNDLE_PLUGIN_PREPEND_EXTENSION = "BNDPrependExtension-";
309
310 private static final String[] EMPTY_STRING_ARRAY =
311 {};
312 private static final String[] DEFAULT_INCLUDES =
313 { "**/**" };
314
315 private static final String NL = System.getProperty( "line.separator" );
316
317
318 protected Maven2OsgiConverter getMaven2OsgiConverter()
319 {
320 return m_maven2OsgiConverter;
321 }
322
323
324 protected MavenProject getProject()
325 {
326 return project;
327 }
328
329 protected DependencyNode buildDependencyGraph( MavenProject mavenProject ) throws MojoExecutionException
330 {
331 DependencyNode dependencyGraph;
332 try
333 {
334 dependencyGraph = m_dependencyGraphBuilder.buildDependencyGraph( mavenProject, null );
335 }
336 catch ( DependencyGraphBuilderException e )
337 {
338 throw new MojoExecutionException( e.getMessage(), e );
339 }
340 return dependencyGraph;
341 }
342
343
344
345
346 public void execute() throws MojoExecutionException
347 {
348 Properties properties = new Properties();
349 String projectType = getProject().getArtifact().getType();
350
351
352 if ( !supportedProjectTypes.contains( projectType ) )
353 {
354 getLog().warn(
355 "Ignoring project type " + projectType + " - supportedProjectTypes = " + supportedProjectTypes );
356 return;
357 }
358
359 execute( getProject(), buildDependencyGraph(getProject()), instructions, properties );
360 }
361
362
363 protected void execute( MavenProject currentProject, DependencyNode dependencyGraph, Map<String, String> originalInstructions, Properties properties )
364 throws MojoExecutionException
365 {
366 try
367 {
368 execute( currentProject, dependencyGraph, originalInstructions, properties, getClasspath( currentProject, dependencyGraph ) );
369 }
370 catch ( IOException e )
371 {
372 throw new MojoExecutionException( "Error calculating classpath for project " + currentProject, e );
373 }
374 }
375
376
377
378 protected static Map<String, String> transformDirectives( Map<String, String> originalInstructions )
379 {
380 Map<String, String> transformedInstructions = new LinkedHashMap<String, String>();
381 for ( Iterator<Map.Entry<String, String>> i = originalInstructions.entrySet().iterator(); i.hasNext(); )
382 {
383 Map.Entry<String, String> e = i.next();
384
385 String key = e.getKey();
386 if ( key.startsWith( "_" ) )
387 {
388 key = "-" + key.substring( 1 );
389 }
390
391 String value = e.getValue();
392 if ( null == value )
393 {
394 value = "";
395 }
396 else
397 {
398 value = value.replaceAll( "\\p{Blank}*[\r\n]\\p{Blank}*", "" );
399 }
400
401 if ( Analyzer.WAB.equals( key ) && value.length() == 0 )
402 {
403
404 value = "src/main/webapp/";
405 }
406
407 transformedInstructions.put( key, value );
408 }
409 return transformedInstructions;
410 }
411
412
413 protected boolean reportErrors( String prefix, Analyzer analyzer )
414 {
415 List<String> errors = analyzer.getErrors();
416 List<String> warnings = analyzer.getWarnings();
417
418 for ( Iterator<String> w = warnings.iterator(); w.hasNext(); )
419 {
420 String msg = w.next();
421 getLog().warn( prefix + " : " + msg );
422 }
423
424 boolean hasErrors = false;
425 String fileNotFound = "Input file does not exist: ";
426 for ( Iterator<String> e = errors.iterator(); e.hasNext(); )
427 {
428 String msg = e.next();
429 if ( msg.startsWith(fileNotFound) && msg.endsWith( "~" ) )
430 {
431
432 String duplicate = Processor.removeDuplicateMarker( msg.substring( fileNotFound.length() ) );
433 getLog().warn( prefix + " : Duplicate path '" + duplicate + "' in Include-Resource" );
434 }
435 else
436 {
437 getLog().error( prefix + " : " + msg );
438 hasErrors = true;
439 }
440 }
441 return hasErrors;
442 }
443
444
445 protected void execute( MavenProject currentProject, DependencyNode dependencyGraph, Map<String, String> originalInstructions, Properties properties,
446 Jar[] classpath ) throws MojoExecutionException
447 {
448 try
449 {
450 File jarFile = new File( getBuildDirectory(), getBundleName( currentProject ) );
451 Builder builder = buildOSGiBundle( currentProject, dependencyGraph, originalInstructions, properties, classpath );
452 boolean hasErrors = reportErrors( "Bundle " + currentProject.getArtifact(), builder );
453 if ( hasErrors )
454 {
455 String failok = builder.getProperty( "-failok" );
456 if ( null == failok || "false".equalsIgnoreCase( failok ) )
457 {
458 jarFile.delete();
459
460 throw new MojoFailureException( "Error(s) found in bundle configuration" );
461 }
462 }
463
464
465 jarFile.getParentFile().mkdirs();
466 builder.getJar().write( jarFile );
467
468 Artifact mainArtifact = currentProject.getArtifact();
469
470 if ( "bundle".equals( mainArtifact.getType() ) )
471 {
472
473 mainArtifact.setArtifactHandler( m_artifactHandlerManager.getArtifactHandler( "jar" ) );
474 }
475
476 boolean customClassifier = null != classifier && classifier.trim().length() > 0;
477 boolean customPackaging = null != packaging && packaging.trim().length() > 0;
478
479 if ( customClassifier && customPackaging )
480 {
481 m_projectHelper.attachArtifact( currentProject, packaging, classifier, jarFile );
482 }
483 else if ( customClassifier )
484 {
485 m_projectHelper.attachArtifact( currentProject, jarFile, classifier );
486 }
487 else if ( customPackaging )
488 {
489 m_projectHelper.attachArtifact( currentProject, packaging, jarFile );
490 }
491 else
492 {
493 mainArtifact.setFile( jarFile );
494 }
495
496 if ( unpackBundle )
497 {
498 unpackBundle( jarFile );
499 }
500
501 if ( manifestLocation != null )
502 {
503 File outputFile = new File( manifestLocation, "MANIFEST.MF" );
504
505 try
506 {
507 ManifestPlugin.writeManifest( builder, outputFile, niceManifest );
508 }
509 catch ( IOException e )
510 {
511 getLog().error( "Error trying to write Manifest to file " + outputFile, e );
512 }
513 }
514
515
516 builder.close();
517 }
518 catch ( MojoFailureException e )
519 {
520 getLog().error( e.getLocalizedMessage() );
521 throw new MojoExecutionException( "Error(s) found in bundle configuration", e );
522 }
523 catch ( Exception e )
524 {
525 getLog().error( "An internal error occurred", e );
526 throw new MojoExecutionException( "Internal error in maven-bundle-plugin", e );
527 }
528 }
529
530
531 protected Builder getOSGiBuilder( MavenProject currentProject, Map<String, String> originalInstructions, Properties properties,
532 Jar[] classpath ) throws Exception
533 {
534 properties.putAll( getDefaultProperties( currentProject ) );
535 properties.putAll( transformDirectives( originalInstructions ) );
536
537
538 final Map<String, String> addProps = new HashMap<String, String>();
539 final Iterator<Map.Entry<Object, Object>> iter = currentProject.getProperties().entrySet().iterator();
540 while ( iter.hasNext() )
541 {
542 final Map.Entry<Object, Object> entry = iter.next();
543 final String key = entry.getKey().toString();
544 if ( key.startsWith(BUNDLE_PLUGIN_EXTENSION) )
545 {
546 final String oKey = key.substring(BUNDLE_PLUGIN_EXTENSION.length());
547 final String currentValue = properties.getProperty(oKey);
548 if ( currentValue == null )
549 {
550 addProps.put(oKey, entry.getValue().toString());
551 }
552 else
553 {
554 addProps.put(oKey, currentValue + ',' + entry.getValue());
555 }
556 }
557 if ( key.startsWith(BUNDLE_PLUGIN_PREPEND_EXTENSION) )
558 {
559 final String oKey = key.substring(BUNDLE_PLUGIN_PREPEND_EXTENSION.length());
560 final String currentValue = properties.getProperty(oKey);
561 if ( currentValue == null )
562 {
563 addProps.put(oKey, entry.getValue().toString());
564 }
565 else
566 {
567 addProps.put(oKey, entry.getValue() + "," + currentValue);
568 }
569 }
570 }
571 properties.putAll( addProps );
572 final Iterator<String> keyIter = addProps.keySet().iterator();
573 while ( keyIter.hasNext() )
574 {
575 Object key = keyIter.next();
576 properties.remove(BUNDLE_PLUGIN_EXTENSION + key);
577 properties.remove(BUNDLE_PLUGIN_PREPEND_EXTENSION + key);
578 }
579
580 if (properties.getProperty("Bundle-Activator") != null
581 && properties.getProperty("Bundle-Activator").isEmpty())
582 {
583 properties.remove("Bundle-Activator");
584 }
585 if (properties.containsKey("-disable-plugin"))
586 {
587 String[] disabled = properties.remove("-disable-plugin").toString().replaceAll(" ", "").split(",");
588 String[] enabled = properties.getProperty(Analyzer.PLUGIN, "").replaceAll(" ", "").split(",");
589 Set<String> plugin = new LinkedHashSet<String>();
590 plugin.addAll(Arrays.asList(enabled));
591 plugin.removeAll(Arrays.asList(disabled));
592 StringBuilder sb = new StringBuilder();
593 for (String s : plugin)
594 {
595 if (sb.length() > 0)
596 {
597 sb.append(",");
598 }
599 sb.append(s);
600 }
601 properties.setProperty(Analyzer.PLUGIN, sb.toString());
602 }
603
604 Builder builder = new Builder();
605 synchronized ( BundlePlugin.class )
606 {
607 builder.setBase( getBase( currentProject ) );
608 }
609 builder.setProperties( sanitize( properties ) );
610 if ( classpath != null )
611 {
612 builder.setClasspath( classpath );
613 }
614
615 return builder;
616 }
617
618
619 protected static Properties sanitize( Properties properties )
620 {
621
622 Properties sanitizedEntries = new Properties();
623 for ( Iterator<Map.Entry<Object,Object>> itr = properties.entrySet().iterator(); itr.hasNext(); )
624 {
625 Map.Entry<Object,Object> entry = itr.next();
626 if ( entry.getKey() instanceof String == false )
627 {
628 String key = sanitize(entry.getKey());
629 if ( !properties.containsKey( key ) )
630 {
631 sanitizedEntries.setProperty( key, sanitize( entry.getValue() ) );
632 }
633 itr.remove();
634 }
635 else if ( entry.getValue() instanceof String == false )
636 {
637 entry.setValue( sanitize( entry.getValue() ) );
638 }
639 }
640 properties.putAll( sanitizedEntries );
641 return properties;
642 }
643
644
645 protected static String sanitize( Object value )
646 {
647 if ( value instanceof String )
648 {
649 return ( String ) value;
650 }
651 else if ( value instanceof Iterable )
652 {
653 String delim = "";
654 StringBuilder buf = new StringBuilder();
655 for ( Object i : ( Iterable<?> ) value )
656 {
657 buf.append( delim ).append( i );
658 delim = ", ";
659 }
660 return buf.toString();
661 }
662 else if ( value.getClass().isArray() )
663 {
664 String delim = "";
665 StringBuilder buf = new StringBuilder();
666 for ( int i = 0, len = Array.getLength( value ); i < len; i++ )
667 {
668 buf.append( delim ).append( Array.get( value, i ) );
669 delim = ", ";
670 }
671 return buf.toString();
672 }
673 else
674 {
675 return String.valueOf( value );
676 }
677 }
678
679
680 protected void addMavenInstructions( MavenProject currentProject, DependencyNode dependencyGraph, Builder builder ) throws Exception
681 {
682 if ( currentProject.getBasedir() != null )
683 {
684
685 includeMavenResources(currentProject, builder, getLog());
686
687
688 addLocalPackages(outputDirectory, builder);
689
690
691 addMavenSourcePath(currentProject, builder, getLog());
692 }
693
694
695 Collection<Artifact> embeddableArtifacts = getEmbeddableArtifacts( currentProject, dependencyGraph, builder );
696 DependencyEmbedder dependencyEmbedder = new DependencyEmbedder(getLog(), dependencyGraph, embeddableArtifacts);
697 dependencyEmbedder.processHeaders(builder);
698
699 Collection<Artifact> embeddedArtifacts = dependencyEmbedder.getEmbeddedArtifacts();
700 if ( !embeddedArtifacts.isEmpty() && createDependencyReducedPom )
701 {
702 Set<String> embeddedIds = new HashSet<String>();
703 for ( Artifact artifact : embeddedArtifacts )
704 {
705 embeddedIds.add( getId( artifact ) );
706 }
707 createDependencyReducedPom( embeddedIds );
708
709 }
710
711 if ( dumpInstructions != null || getLog().isDebugEnabled() )
712 {
713 StringBuilder buf = new StringBuilder();
714 getLog().debug( "BND Instructions:" + NL + dumpInstructions( builder.getProperties(), buf ) );
715 if ( dumpInstructions != null )
716 {
717 getLog().info( "Writing BND instructions to " + dumpInstructions );
718 dumpInstructions.getParentFile().mkdirs();
719 FileUtils.fileWrite( dumpInstructions, "# BND instructions" + NL + buf );
720 }
721 }
722
723
724
725 if ( dumpClasspath != null || getLog().isDebugEnabled() )
726 {
727 StringBuilder buf = new StringBuilder();
728 getLog().debug("BND Classpath:" + NL + dumpClasspath(builder.getClasspath(), buf));
729 if ( dumpClasspath != null )
730 {
731 getLog().info( "Writing BND classpath to " + dumpClasspath );
732 dumpClasspath.getParentFile().mkdirs();
733 FileUtils.fileWrite( dumpClasspath, "# BND classpath" + NL + buf );
734 }
735 }
736 }
737
738
739
740
741 private void createDependencyReducedPom( Set<String> artifactsToRemove )
742 throws IOException, DependencyTreeBuilderException, ProjectBuildingException
743 {
744 Model model = project.getOriginalModel();
745 List<Dependency> dependencies = new ArrayList<Dependency>();
746
747 boolean modified = false;
748
749 List<Dependency> transitiveDeps = new ArrayList<Dependency>();
750
751 for ( Iterator it = project.getArtifacts().iterator(); it.hasNext(); )
752 {
753 Artifact artifact = (Artifact) it.next();
754
755 if ( "pom".equals( artifact.getType() ) )
756 {
757
758 continue;
759 }
760
761
762 Dependency dep = new Dependency();
763 dep.setArtifactId( artifact.getArtifactId() );
764 if ( artifact.hasClassifier() )
765 {
766 dep.setClassifier( artifact.getClassifier() );
767 }
768 dep.setGroupId( artifact.getGroupId() );
769 dep.setOptional( artifact.isOptional() );
770 dep.setScope( artifact.getScope() );
771 dep.setType( artifact.getType() );
772 dep.setVersion( artifact.getVersion() );
773
774
775
776 transitiveDeps.add( dep );
777 }
778 List<Dependency> origDeps = project.getDependencies();
779
780 for ( Iterator<Dependency> i = origDeps.iterator(); i.hasNext(); )
781 {
782 Dependency d = i.next();
783
784 dependencies.add( d );
785
786 String id = getId( d );
787
788 if ( artifactsToRemove.contains( id ) )
789 {
790 modified = true;
791
792 dependencies.remove( d );
793 }
794 }
795
796
797 if ( modified )
798 {
799 while ( modified )
800 {
801
802 model.setDependencies( dependencies );
803
804 if ( dependencyReducedPomLocation == null )
805 {
806
807 dependencyReducedPomLocation = new File ( project.getBasedir(), "dependency-reduced-pom.xml" );
808 }
809
810 File f = dependencyReducedPomLocation;
811 if ( f.exists() )
812 {
813 f.delete();
814 }
815
816 Writer w = WriterFactory.newXmlWriter( f );
817
818 String origRelativePath = null;
819 String replaceRelativePath = null;
820 if ( model.getParent() != null)
821 {
822 origRelativePath = model.getParent().getRelativePath();
823
824 }
825 replaceRelativePath = origRelativePath;
826
827 if ( origRelativePath == null )
828 {
829 origRelativePath = "../pom.xml";
830 }
831
832 if ( model.getParent() != null )
833 {
834 File parentFile = new File( project.getBasedir(), model.getParent().getRelativePath() ).getCanonicalFile();
835 if ( !parentFile.isFile() )
836 {
837 parentFile = new File( parentFile, "pom.xml");
838 }
839
840 parentFile = parentFile.getCanonicalFile();
841
842 String relPath = RelativizePath.convertToRelativePath( parentFile, f );
843 model.getParent().setRelativePath( relPath );
844 }
845
846 try
847 {
848 PomWriter.write( w, model, true );
849 }
850 finally
851 {
852 if ( model.getParent() != null )
853 {
854 model.getParent().setRelativePath( replaceRelativePath );
855 }
856 w.close();
857 }
858
859 MavenProject p2 = mavenProjectBuilder.build( f, localRepository, null );
860 modified = updateExcludesInDeps( p2, dependencies, transitiveDeps );
861
862 }
863
864 project.setFile( dependencyReducedPomLocation );
865 }
866 }
867
868 private String getId( Artifact artifact )
869 {
870 return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier() );
871 }
872
873 private String getId( Dependency dependency )
874 {
875 return getId( dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(),
876 dependency.getClassifier() );
877 }
878
879 private String getId( String groupId, String artifactId, String type, String classifier )
880 {
881 return groupId + ":" + artifactId + ":" + type + ":" + ( ( classifier != null ) ? classifier : "" );
882 }
883
884 public boolean updateExcludesInDeps( MavenProject project, List<Dependency> dependencies, List<Dependency> transitiveDeps )
885 throws DependencyTreeBuilderException
886 {
887 org.apache.maven.shared.dependency.tree.DependencyNode node = dependencyTreeBuilder.buildDependencyTree(project, localRepository, artifactFactory,
888 artifactMetadataSource, null,
889 artifactCollector);
890 boolean modified = false;
891 Iterator it = node.getChildren().listIterator();
892 while ( it.hasNext() )
893 {
894 org.apache.maven.shared.dependency.tree.DependencyNode n2 = (org.apache.maven.shared.dependency.tree.DependencyNode) it.next();
895 Iterator it2 = n2.getChildren().listIterator();
896 while ( it2.hasNext() )
897 {
898 org.apache.maven.shared.dependency.tree.DependencyNode n3 = (org.apache.maven.shared.dependency.tree.DependencyNode) it2.next();
899
900
901
902 if ( n3.getState() == org.apache.maven.shared.dependency.tree.DependencyNode.INCLUDED )
903 {
904
905
906
907
908
909
910 boolean found = false;
911 for ( int x = 0; x < transitiveDeps.size(); x++ )
912 {
913 Dependency dep = transitiveDeps.get( x );
914 if ( dep.getArtifactId().equals( n3.getArtifact().getArtifactId() ) && dep.getGroupId().equals(
915 n3.getArtifact().getGroupId() ) )
916 {
917 found = true;
918 }
919
920 }
921
922 if ( !found )
923 {
924 for ( int x = 0; x < dependencies.size(); x++ )
925 {
926 Dependency dep = dependencies.get( x );
927 if ( dep.getArtifactId().equals( n2.getArtifact().getArtifactId() )
928 && dep.getGroupId().equals( n2.getArtifact().getGroupId() ) )
929 {
930 Exclusion exclusion = new Exclusion();
931 exclusion.setArtifactId( n3.getArtifact().getArtifactId() );
932 exclusion.setGroupId( n3.getArtifact().getGroupId() );
933 dep.addExclusion( exclusion );
934 modified = true;
935 break;
936 }
937 }
938 }
939 }
940 }
941 }
942 return modified;
943 }
944
945
946 protected Builder buildOSGiBundle( MavenProject currentProject, DependencyNode dependencyGraph, Map<String, String> originalInstructions, Properties properties,
947 Jar[] classpath ) throws Exception
948 {
949 Builder builder = getOSGiBuilder( currentProject, originalInstructions, properties, classpath );
950
951 addMavenInstructions( currentProject, dependencyGraph, builder );
952
953 builder.build();
954
955 mergeMavenManifest(currentProject, dependencyGraph, builder);
956
957 return builder;
958 }
959
960
961 protected static StringBuilder dumpInstructions( Properties properties, StringBuilder buf )
962 {
963 try
964 {
965 buf.append( "#-----------------------------------------------------------------------" + NL );
966 Properties stringProperties = new Properties();
967 for ( Enumeration<String> e = (Enumeration<String>) properties.propertyNames(); e.hasMoreElements(); )
968 {
969
970 String key = e.nextElement();
971 String value = properties.getProperty( key );
972 if ( value != null )
973 {
974 stringProperties.setProperty( key, value );
975 }
976 }
977 ByteArrayOutputStream out = new ByteArrayOutputStream();
978 stringProperties.store( out, null );
979 buf.append( out.toString( "8859_1" ) );
980 buf.append( "#-----------------------------------------------------------------------" + NL );
981 }
982 catch ( Throwable e )
983 {
984
985 }
986 return buf;
987 }
988
989
990 protected static StringBuilder dumpClasspath( List<Jar> classpath, StringBuilder buf )
991 {
992 try
993 {
994 buf.append("#-----------------------------------------------------------------------" + NL);
995 buf.append( "-classpath:\\" + NL );
996 for ( Iterator<Jar> i = classpath.iterator(); i.hasNext(); )
997 {
998 File path = i.next().getSource();
999 if ( path != null )
1000 {
1001 buf.append( ' ' + path.toString() + ( i.hasNext() ? ",\\" : "" ) + NL );
1002 }
1003 }
1004 buf.append( "#-----------------------------------------------------------------------" + NL );
1005 }
1006 catch ( Throwable e )
1007 {
1008
1009 }
1010 return buf;
1011 }
1012
1013
1014 protected static StringBuilder dumpManifest( Manifest manifest, StringBuilder buf )
1015 {
1016 try
1017 {
1018 buf.append( "#-----------------------------------------------------------------------" + NL );
1019 ByteArrayOutputStream out = new ByteArrayOutputStream();
1020 ManifestWriter.outputManifest(manifest, out, true);
1021 buf.append( out.toString( "UTF8" ) );
1022 buf.append( "#-----------------------------------------------------------------------" + NL );
1023 }
1024 catch ( Throwable e )
1025 {
1026
1027 }
1028 return buf;
1029 }
1030
1031
1032 protected static void includeMavenResources( MavenProject currentProject, Analyzer analyzer, Log log )
1033 {
1034
1035 final String mavenResourcePaths = getMavenResourcePaths( currentProject, false );
1036 final String mavenTestResourcePaths = getMavenResourcePaths( currentProject, true );
1037 final String includeResource = analyzer.getProperty( Analyzer.INCLUDE_RESOURCE );
1038 if ( includeResource != null )
1039 {
1040 if ( includeResource.contains( MAVEN_RESOURCES ) || includeResource.contains( MAVEN_TEST_RESOURCES ) )
1041 {
1042 String combinedResource = StringUtils.replace( includeResource, MAVEN_RESOURCES, mavenResourcePaths );
1043 combinedResource = StringUtils.replace( combinedResource, MAVEN_TEST_RESOURCES, mavenTestResourcePaths );
1044 if ( combinedResource.length() > 0 )
1045 {
1046 analyzer.setProperty( Analyzer.INCLUDE_RESOURCE, combinedResource );
1047 }
1048 else
1049 {
1050 analyzer.unsetProperty( Analyzer.INCLUDE_RESOURCE );
1051 }
1052 }
1053 else if ( mavenResourcePaths.length() > 0 )
1054 {
1055 log.warn( Analyzer.INCLUDE_RESOURCE + ": overriding " + mavenResourcePaths + " with " + includeResource
1056 + " (add " + MAVEN_RESOURCES + " if you want to include the maven resources)" );
1057 }
1058 }
1059 else if ( mavenResourcePaths.length() > 0 )
1060 {
1061 analyzer.setProperty( Analyzer.INCLUDE_RESOURCE, mavenResourcePaths );
1062 }
1063 }
1064
1065
1066 protected void mergeMavenManifest( MavenProject currentProject, DependencyNode dependencyGraph, Builder builder ) throws Exception
1067 {
1068 Jar jar = builder.getJar();
1069
1070 if ( getLog().isDebugEnabled() )
1071 {
1072 getLog().debug( "BND Manifest:" + NL + dumpManifest( jar.getManifest(), new StringBuilder() ) );
1073 }
1074
1075 boolean addMavenDescriptor = currentProject.getBasedir() != null;
1076
1077 try
1078 {
1079
1080
1081
1082 MavenArchiveConfiguration archiveConfig = JarPluginConfiguration.getArchiveConfiguration( currentProject );
1083 String mavenManifestText = new MavenArchiver().getManifest( currentProject, archiveConfig ).toString();
1084 addMavenDescriptor = addMavenDescriptor && archiveConfig.isAddMavenDescriptor();
1085
1086 Manifest mavenManifest = new Manifest();
1087
1088
1089 File externalManifestFile = archiveConfig.getManifestFile();
1090 if ( null != externalManifestFile )
1091 {
1092 if ( !externalManifestFile.isAbsolute() )
1093 {
1094 externalManifestFile = new File( currentProject.getBasedir(), externalManifestFile.getPath() );
1095 }
1096 if ( externalManifestFile.exists() && !externalManifestFile.equals( new File( manifestLocation, "MANIFEST.MF" ) ) )
1097 {
1098 InputStream mis = new FileInputStream( externalManifestFile );
1099 mavenManifest.read( mis );
1100 mis.close();
1101 }
1102 }
1103
1104
1105 mavenManifest.read( new ByteArrayInputStream( mavenManifestText.getBytes( "UTF8" ) ) );
1106
1107 if ( !archiveConfig.isManifestSectionsEmpty() )
1108 {
1109
1110
1111
1112 List<ManifestSection> sections = archiveConfig.getManifestSections();
1113 for ( Iterator<ManifestSection> i = sections.iterator(); i.hasNext(); )
1114 {
1115 ManifestSection section = i.next();
1116 Attributes attributes = new Attributes();
1117
1118 if ( !section.isManifestEntriesEmpty() )
1119 {
1120 Map<String, String> entries = section.getManifestEntries();
1121 for ( Iterator<Map.Entry<String, String>> j = entries.entrySet().iterator(); j.hasNext(); )
1122 {
1123 Map.Entry<String, String> entry = j.next();
1124 attributes.putValue( entry.getKey(), entry.getValue() );
1125 }
1126 }
1127
1128 mavenManifest.getEntries().put( section.getName(), attributes );
1129 }
1130 }
1131
1132 Attributes mainMavenAttributes = mavenManifest.getMainAttributes();
1133 mainMavenAttributes.putValue( "Created-By", "Apache Maven Bundle Plugin" );
1134
1135 String[] removeHeaders = builder.getProperty( Constants.REMOVEHEADERS, "" ).split( "," );
1136
1137
1138 for ( int i = 0; i < removeHeaders.length; i++ )
1139 {
1140 for ( Iterator<Object> j = mainMavenAttributes.keySet().iterator(); j.hasNext(); )
1141 {
1142 if ( j.next().toString().matches( removeHeaders[i].trim() ) )
1143 {
1144 j.remove();
1145 }
1146 }
1147 }
1148
1149
1150
1151
1152 Properties properties = builder.getProperties();
1153 Manifest bundleManifest = jar.getManifest();
1154 if ( properties.containsKey( "Merge-Headers" ) )
1155 {
1156 Instructions instructions = new Instructions( ExtList.from(builder.getProperty("Merge-Headers")) );
1157 mergeManifest( instructions, bundleManifest, mavenManifest );
1158 }
1159 else
1160 {
1161 bundleManifest.getMainAttributes().putAll( mainMavenAttributes );
1162 bundleManifest.getEntries().putAll( mavenManifest.getEntries() );
1163 }
1164
1165
1166
1167 String importPackages = bundleManifest.getMainAttributes().getValue( "Import-Package" );
1168 if ( importPackages != null )
1169 {
1170 Set optionalPackages = getOptionalPackages( currentProject, dependencyGraph );
1171
1172 Map<String, ? extends Map<String, String>> values = new Analyzer().parseHeader( importPackages );
1173 for ( Map.Entry<String, ? extends Map<String, String>> entry : values.entrySet() )
1174 {
1175 String pkg = entry.getKey();
1176 Map<String, String> options = entry.getValue();
1177 if ( !options.containsKey( "resolution:" ) && optionalPackages.contains( pkg ) )
1178 {
1179 options.put( "resolution:", "optional" );
1180 }
1181 }
1182 String result = Processor.printClauses( values );
1183 bundleManifest.getMainAttributes().putValue( "Import-Package", result );
1184 }
1185
1186 jar.setManifest( bundleManifest );
1187 }
1188 catch ( Exception e )
1189 {
1190 getLog().warn( "Unable to merge Maven manifest: " + e.getLocalizedMessage() );
1191 }
1192
1193 if ( addMavenDescriptor )
1194 {
1195 doMavenMetadata( currentProject, jar );
1196 }
1197
1198 if ( getLog().isDebugEnabled() )
1199 {
1200 getLog().debug( "Final Manifest:" + NL + dumpManifest( jar.getManifest(), new StringBuilder() ) );
1201 }
1202
1203 builder.setJar( jar );
1204 }
1205
1206
1207 protected static void mergeManifest( Instructions instructions, Manifest... manifests ) throws IOException
1208 {
1209 for ( int i = manifests.length - 2; i >= 0; i-- )
1210 {
1211 Manifest mergedManifest = manifests[i];
1212 Manifest manifest = manifests[i + 1];
1213 Attributes mergedMainAttributes = mergedManifest.getMainAttributes();
1214 Attributes mainAttributes = manifest.getMainAttributes();
1215 Attributes filteredMainAttributes = filterAttributes( instructions, mainAttributes, null );
1216 if ( !filteredMainAttributes.isEmpty() )
1217 {
1218 mergeAttributes( mergedMainAttributes, filteredMainAttributes );
1219 }
1220 Map<String, Attributes> mergedEntries = mergedManifest.getEntries();
1221 Map<String, Attributes> entries = manifest.getEntries();
1222 for ( Map.Entry<String, Attributes> entry : entries.entrySet() )
1223 {
1224 String name = entry.getKey();
1225 Attributes attributes = entry.getValue();
1226 Attributes filteredAttributes = filterAttributes( instructions, attributes, null );
1227 if ( !filteredAttributes.isEmpty() )
1228 {
1229 Attributes mergedAttributes = mergedManifest.getAttributes( name );
1230 if ( mergedAttributes != null)
1231 {
1232 mergeAttributes(mergedAttributes, filteredAttributes);
1233 }
1234 else
1235 {
1236 mergedEntries.put(name, filteredAttributes);
1237 }
1238 }
1239 }
1240 }
1241 }
1242
1243
1244
1245
1246
1247 private static Attributes filterAttributes(Instructions instructions, Attributes source, Set<Instruction> nomatch) {
1248 Attributes result = new Attributes();
1249 Map<String, Object> keys = new TreeMap<String, Object>();
1250 for ( Object key : source.keySet() )
1251 {
1252 keys.put( key.toString(), key );
1253 }
1254
1255 List<Instruction> filters = new ArrayList<Instruction>( instructions.keySet() );
1256 if (nomatch == null)
1257 {
1258 nomatch = Create.set();
1259 }
1260 for ( Instruction instruction : filters ) {
1261 boolean match = false;
1262 for (Iterator<Map.Entry<String, Object>> i = keys.entrySet().iterator(); i.hasNext();)
1263 {
1264 Map.Entry<String, Object> entry = i.next();
1265 String key = entry.getKey();
1266 if ( instruction.matches( key ) )
1267 {
1268 match = true;
1269 if (!instruction.isNegated()) {
1270 Object name = entry.getValue();
1271 Object value = source.get( name );
1272 result.put( name, value );
1273 }
1274 i.remove();
1275 }
1276 }
1277 if (!match && !instruction.isAny())
1278 nomatch.add(instruction);
1279 }
1280
1281
1282
1283
1284
1285
1286
1287
1288 for (Iterator<Instruction> i = nomatch.iterator(); i.hasNext();) {
1289 Instruction instruction = i.next();
1290
1291
1292
1293
1294
1295 if (instruction.isLiteral() && !instruction.isNegated()) {
1296 Object key = keys.get( instruction.getLiteral() );
1297 if ( key != null )
1298 {
1299 Object value = source.get( key );
1300 result.put( key, value );
1301 }
1302 i.remove();
1303 continue;
1304 }
1305
1306
1307
1308
1309
1310 if (instruction.isNegated()) {
1311 i.remove();
1312 continue;
1313 }
1314
1315
1316
1317 if (instruction.isOptional()) {
1318 i.remove();
1319 continue;
1320 }
1321 }
1322 return result;
1323 }
1324
1325
1326 private static void mergeAttributes( Attributes... attributesArray ) throws IOException
1327 {
1328 for ( int i = attributesArray.length - 2; i >= 0; i-- )
1329 {
1330 Attributes mergedAttributes = attributesArray[i];
1331 Attributes attributes = attributesArray[i + 1];
1332 for ( Map.Entry<Object, Object> entry : attributes.entrySet() )
1333 {
1334 Object name = entry.getKey();
1335 String value = (String) entry.getValue();
1336 String oldValue = (String) mergedAttributes.put( name, value );
1337 if ( oldValue != null )
1338 {
1339 Parameters mergedClauses = OSGiHeader.parseHeader(oldValue);
1340 Parameters clauses = OSGiHeader.parseHeader( value );
1341 if ( !mergedClauses.isEqual( clauses) )
1342 {
1343 for ( Map.Entry<String, Attrs> clauseEntry : clauses.entrySet() )
1344 {
1345 String clause = clauseEntry.getKey();
1346 Attrs attrs = clauseEntry.getValue();
1347 Attrs mergedAttrs = mergedClauses.get( clause );
1348 if ( mergedAttrs == null)
1349 {
1350 mergedClauses.put( clause, attrs );
1351 }
1352 else if ( !mergedAttrs.isEqual(attrs) )
1353 {
1354 for ( Map.Entry<String,String> adentry : attrs.entrySet() )
1355 {
1356 String adname = adentry.getKey();
1357 String ad = adentry.getValue();
1358 if ( mergedAttrs.containsKey( adname ) )
1359 {
1360 Attrs.Type type = attrs.getType( adname );
1361 switch (type)
1362 {
1363 case VERSIONS:
1364 case STRINGS:
1365 case LONGS:
1366 case DOUBLES:
1367 ExtList<String> mergedAd = ExtList.from( mergedAttrs.get( adname ) );
1368 ExtList.from( ad ).addAll( ExtList.from( ad ) );
1369 mergedAttrs.put(adname, mergedAd.join() );
1370 break;
1371 }
1372 }
1373 else
1374 {
1375 mergedAttrs.put( adname, ad );
1376 }
1377 }
1378 }
1379 }
1380 mergedAttributes.put( name, Processor.printClauses( mergedClauses ) );
1381 }
1382 }
1383 }
1384 }
1385 }
1386
1387
1388 protected Set<String> getOptionalPackages( MavenProject currentProject, DependencyNode dependencyGraph ) throws IOException, MojoExecutionException
1389 {
1390 ArrayList<Artifact> inscope = new ArrayList<Artifact>();
1391 final Collection<Artifact> artifacts = getSelectedDependencies( dependencyGraph, currentProject.getArtifacts() );
1392 for ( Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); )
1393 {
1394 Artifact artifact = it.next();
1395 if ( artifact.getArtifactHandler().isAddedToClasspath() )
1396 {
1397 inscope.add( artifact );
1398 }
1399 }
1400
1401 HashSet<String> optionalArtifactIds = new HashSet<String>();
1402 for ( Iterator<Artifact> it = inscope.iterator(); it.hasNext(); )
1403 {
1404 Artifact artifact = it.next();
1405 if ( artifact.isOptional() )
1406 {
1407 String id = artifact.toString();
1408 if ( artifact.getScope() != null )
1409 {
1410
1411 id = id.replaceFirst( ":[^:]*$", "" );
1412 }
1413 optionalArtifactIds.add( id );
1414 }
1415
1416 }
1417
1418 HashSet<String> required = new HashSet<String>();
1419 HashSet<String> optional = new HashSet<String>();
1420 for ( Iterator<Artifact> it = inscope.iterator(); it.hasNext(); )
1421 {
1422 Artifact artifact = it.next();
1423 File file = getFile( artifact );
1424 if ( file == null )
1425 {
1426 continue;
1427 }
1428
1429 Jar jar = new Jar( artifact.getArtifactId(), file );
1430 if ( isTransitivelyOptional( optionalArtifactIds, artifact ) )
1431 {
1432 optional.addAll( jar.getPackages() );
1433 }
1434 else
1435 {
1436 required.addAll( jar.getPackages() );
1437 }
1438 jar.close();
1439 }
1440
1441 optional.removeAll( required );
1442 return optional;
1443 }
1444
1445
1446
1447
1448
1449
1450
1451
1452 protected boolean isTransitivelyOptional( HashSet<String> optionalArtifactIds, Artifact artifact )
1453 {
1454 List<String> trail = artifact.getDependencyTrail();
1455 for ( Iterator<String> iterator = trail.iterator(); iterator.hasNext(); )
1456 {
1457 String next = iterator.next();
1458 if ( optionalArtifactIds.contains( next ) )
1459 {
1460 return true;
1461 }
1462 }
1463 return false;
1464 }
1465
1466
1467 private void unpackBundle( File jarFile )
1468 {
1469 File outputDir = getOutputDirectory();
1470 if ( null == outputDir )
1471 {
1472 outputDir = new File( getBuildDirectory(), "classes" );
1473 }
1474
1475 try
1476 {
1477
1478
1479
1480
1481 if ( !outputDir.exists() )
1482 {
1483 outputDir.mkdirs();
1484 }
1485
1486 UnArchiver unArchiver = m_archiverManager.getUnArchiver( "jar" );
1487 unArchiver.setDestDirectory( outputDir );
1488 unArchiver.setSourceFile( jarFile );
1489 unArchiver.extract();
1490 }
1491 catch ( Exception e )
1492 {
1493 getLog().error( "Problem unpacking " + jarFile + " to " + outputDir, e );
1494 }
1495 }
1496
1497
1498 protected static String removeTagFromInstruction( String instruction, String tag )
1499 {
1500 StringBuffer buf = new StringBuffer();
1501
1502 String[] clauses = instruction.split( "," );
1503 for ( int i = 0; i < clauses.length; i++ )
1504 {
1505 String clause = clauses[i].trim();
1506 if ( !tag.equals( clause ) )
1507 {
1508 if ( buf.length() > 0 )
1509 {
1510 buf.append( ',' );
1511 }
1512 buf.append( clause );
1513 }
1514 }
1515
1516 return buf.toString();
1517 }
1518
1519
1520 private static Map<String, String> getProperties( Model projectModel, String prefix )
1521 {
1522 Map<String, String> properties = new LinkedHashMap<String, String>();
1523 Method methods[] = Model.class.getDeclaredMethods();
1524 for ( int i = 0; i < methods.length; i++ )
1525 {
1526 String name = methods[i].getName();
1527 if ( name.startsWith( "get" ) )
1528 {
1529 try
1530 {
1531 Object v = methods[i].invoke( projectModel, null );
1532 if ( v != null )
1533 {
1534 name = prefix + Character.toLowerCase( name.charAt( 3 ) ) + name.substring( 4 );
1535 if ( v.getClass().isArray() )
1536 properties.put( name, Arrays.asList( ( Object[] ) v ).toString() );
1537 else
1538 properties.put( name, v.toString() );
1539
1540 }
1541 }
1542 catch ( Exception e )
1543 {
1544
1545 }
1546 }
1547 }
1548 return properties;
1549 }
1550
1551
1552 private static StringBuffer printLicenses( List<License> licenses )
1553 {
1554 if ( licenses == null || licenses.size() == 0 )
1555 return null;
1556 StringBuffer sb = new StringBuffer();
1557 String del = "";
1558 for ( Iterator<License> i = licenses.iterator(); i.hasNext(); )
1559 {
1560 License l = i.next();
1561 String url = l.getUrl();
1562 if ( url == null )
1563 continue;
1564 sb.append( del );
1565 sb.append( url );
1566 del = ", ";
1567 }
1568 if ( sb.length() == 0 )
1569 return null;
1570 return sb;
1571 }
1572
1573
1574
1575
1576
1577
1578 private void doMavenMetadata( MavenProject currentProject, Jar jar ) throws IOException
1579 {
1580 String path = "META-INF/maven/" + currentProject.getGroupId() + "/" + currentProject.getArtifactId();
1581
1582 File pomFile = currentProject.getFile();
1583 if ( pomFile == null || !pomFile.exists() )
1584 {
1585 pomFile = new File( currentProject.getBasedir(), "pom.xml" );
1586 }
1587 if ( pomFile.exists() )
1588 {
1589 jar.putResource( path + "/pom.xml", new FileResource( pomFile ) );
1590 }
1591
1592 Properties p = new Properties();
1593 p.put( "version", currentProject.getVersion() );
1594 p.put( "groupId", currentProject.getGroupId() );
1595 p.put( "artifactId", currentProject.getArtifactId() );
1596 ByteArrayOutputStream out = new ByteArrayOutputStream();
1597 p.store( out, "Generated by org.apache.felix.bundleplugin" );
1598 jar.putResource( path + "/pom.properties", new EmbeddedResource( out.toByteArray(), System.currentTimeMillis() ) );
1599 }
1600
1601
1602 protected Jar[] getClasspath( MavenProject currentProject, DependencyNode dependencyGraph ) throws IOException, MojoExecutionException
1603 {
1604 List<Jar> list = new ArrayList<Jar>();
1605
1606 if ( getOutputDirectory() != null && getOutputDirectory().exists() )
1607 {
1608 list.add( new Jar( ".", getOutputDirectory() ) );
1609 }
1610
1611 final Collection<Artifact> artifacts = getSelectedDependencies( dependencyGraph, currentProject.getArtifacts() );
1612 for ( Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); )
1613 {
1614 Artifact artifact = it.next();
1615 if ( artifact.getArtifactHandler().isAddedToClasspath() && !Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
1616 {
1617 File file = getFile( artifact );
1618 if ( file == null )
1619 {
1620 getLog().warn(
1621 "File is not available for artifact " + artifact + " in project "
1622 + currentProject.getArtifact() );
1623 continue;
1624 }
1625 Jar jar = new Jar( artifact.getArtifactId(), file );
1626 list.add( jar );
1627 }
1628 }
1629 Jar[] cp = new Jar[list.size()];
1630 list.toArray( cp );
1631 return cp;
1632 }
1633
1634
1635 private Collection<Artifact> getSelectedDependencies( DependencyNode dependencyGraph, Collection<Artifact> artifacts ) throws MojoExecutionException
1636 {
1637 if ( null == excludeDependencies || excludeDependencies.length() == 0 )
1638 {
1639 return artifacts;
1640 }
1641 else if ( "true".equalsIgnoreCase( excludeDependencies ) )
1642 {
1643 return Collections.emptyList();
1644 }
1645
1646 Collection<Artifact> selectedDependencies = new LinkedHashSet<Artifact>( artifacts );
1647 DependencyExcluder excluder = new DependencyExcluder( dependencyGraph, artifacts );
1648 excluder.processHeaders( excludeDependencies );
1649 selectedDependencies.removeAll( excluder.getExcludedArtifacts() );
1650
1651 return selectedDependencies;
1652 }
1653
1654
1655
1656
1657
1658
1659
1660 protected File getFile( Artifact artifact )
1661 {
1662 return artifact.getFile();
1663 }
1664
1665
1666 private static void header( Properties properties, String key, Object value )
1667 {
1668 if ( value == null )
1669 return;
1670
1671 if ( value instanceof Collection && ( ( Collection ) value ).isEmpty() )
1672 return;
1673
1674 properties.put( key, value.toString().replaceAll( "[\r\n]", "" ) );
1675 }
1676
1677
1678
1679
1680
1681
1682
1683
1684 protected String convertVersionToOsgi( String version )
1685 {
1686 return getMaven2OsgiConverter().getVersion( version );
1687 }
1688
1689
1690
1691
1692
1693 protected String getBundleName( MavenProject currentProject )
1694 {
1695 String extension;
1696 try
1697 {
1698 extension = currentProject.getArtifact().getArtifactHandler().getExtension();
1699 }
1700 catch ( Throwable e )
1701 {
1702 extension = currentProject.getArtifact().getType();
1703 }
1704 if ( StringUtils.isEmpty( extension ) || "bundle".equals( extension ) || "pom".equals( extension ) )
1705 {
1706 extension = "jar";
1707 }
1708 if ( null != classifier && classifier.trim().length() > 0 )
1709 {
1710 return finalName + '-' + classifier + '.' + extension;
1711 }
1712 return finalName + '.' + extension;
1713 }
1714
1715
1716 protected String getBuildDirectory()
1717 {
1718 return buildDirectory;
1719 }
1720
1721
1722 protected void setBuildDirectory( String _buildirectory )
1723 {
1724 buildDirectory = _buildirectory;
1725 }
1726
1727
1728 protected Properties getDefaultProperties( MavenProject currentProject )
1729 {
1730 Properties properties = new Properties();
1731
1732 String bsn;
1733 try
1734 {
1735 bsn = getMaven2OsgiConverter().getBundleSymbolicName( currentProject.getArtifact() );
1736 }
1737 catch ( Exception e )
1738 {
1739 bsn = currentProject.getGroupId() + "." + currentProject.getArtifactId();
1740 }
1741
1742
1743 properties.put( MAVEN_SYMBOLICNAME, bsn );
1744 properties.put( Analyzer.BUNDLE_SYMBOLICNAME, bsn );
1745 properties.put( Analyzer.IMPORT_PACKAGE, "*" );
1746 properties.put( Analyzer.BUNDLE_VERSION, getMaven2OsgiConverter().getVersion( currentProject.getVersion() ) );
1747
1748
1749 properties.put( Constants.REMOVEHEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE );
1750
1751 header( properties, Analyzer.BUNDLE_DESCRIPTION, currentProject.getDescription() );
1752 StringBuffer licenseText = printLicenses( currentProject.getLicenses() );
1753 if ( licenseText != null )
1754 {
1755 header( properties, Analyzer.BUNDLE_LICENSE, licenseText );
1756 }
1757 header( properties, Analyzer.BUNDLE_NAME, currentProject.getName() );
1758
1759 if ( currentProject.getOrganization() != null )
1760 {
1761 if ( currentProject.getOrganization().getName() != null )
1762 {
1763 String organizationName = currentProject.getOrganization().getName();
1764 header( properties, Analyzer.BUNDLE_VENDOR, organizationName );
1765 properties.put( "project.organization.name", organizationName );
1766 properties.put( "pom.organization.name", organizationName );
1767 }
1768 if ( currentProject.getOrganization().getUrl() != null )
1769 {
1770 String organizationUrl = currentProject.getOrganization().getUrl();
1771 header( properties, Analyzer.BUNDLE_DOCURL, organizationUrl );
1772 properties.put( "project.organization.url", organizationUrl );
1773 properties.put( "pom.organization.url", organizationUrl );
1774 }
1775 }
1776
1777 properties.putAll( currentProject.getProperties() );
1778 properties.putAll( currentProject.getModel().getProperties() );
1779
1780 for ( Iterator<String> i = currentProject.getFilters().iterator(); i.hasNext(); )
1781 {
1782 File filterFile = new File( i.next() );
1783 if ( filterFile.isFile() )
1784 {
1785 properties.putAll( PropertyUtils.loadProperties( filterFile ) );
1786 }
1787 }
1788
1789 if ( m_mavenSession != null )
1790 {
1791 try
1792 {
1793
1794 Properties sessionProperties = m_mavenSession.getExecutionProperties();
1795 for ( Enumeration<String> e = (Enumeration<String>) sessionProperties.propertyNames(); e.hasMoreElements(); )
1796 {
1797 String key = e.nextElement();
1798 if ( key.length() > 0 && !Character.isUpperCase( key.charAt( 0 ) ) )
1799 {
1800 properties.put( key, sessionProperties.getProperty( key ) );
1801 }
1802 }
1803 }
1804 catch ( Exception e )
1805 {
1806 getLog().warn( "Problem with Maven session properties: " + e.getLocalizedMessage() );
1807 }
1808 }
1809
1810 properties.putAll( getProperties( currentProject.getModel(), "project.build." ) );
1811 properties.putAll( getProperties( currentProject.getModel(), "pom." ) );
1812 properties.putAll( getProperties( currentProject.getModel(), "project." ) );
1813
1814 properties.put( "project.baseDir", getBase( currentProject ) );
1815 properties.put( "project.build.directory", getBuildDirectory() );
1816 properties.put( "project.build.outputdirectory", getOutputDirectory() );
1817
1818 properties.put( "classifier", classifier == null ? "" : classifier );
1819
1820
1821 header( properties, Analyzer.PLUGIN, BlueprintPlugin.class.getName() + ","
1822 + SpringXMLType.class.getName() );
1823
1824 return properties;
1825 }
1826
1827
1828 protected static File getBase( MavenProject currentProject )
1829 {
1830 return currentProject.getBasedir() != null ? currentProject.getBasedir() : new File( "" );
1831 }
1832
1833
1834 protected File getOutputDirectory()
1835 {
1836 return outputDirectory;
1837 }
1838
1839
1840 protected void setOutputDirectory( File _outputDirectory )
1841 {
1842 outputDirectory = _outputDirectory;
1843 }
1844
1845
1846 private static void addLocalPackages( File outputDirectory, Analyzer analyzer ) throws IOException
1847 {
1848 Packages packages = new Packages();
1849
1850 if ( outputDirectory != null && outputDirectory.isDirectory() )
1851 {
1852
1853 DirectoryScanner scanner = new DirectoryScanner();
1854 scanner.setBasedir( outputDirectory );
1855 scanner.setIncludes( new String[]
1856 { "**/*.class" } );
1857
1858 scanner.addDefaultExcludes();
1859 scanner.scan();
1860
1861 String[] paths = scanner.getIncludedFiles();
1862 for ( int i = 0; i < paths.length; i++ )
1863 {
1864 packages.put( analyzer.getPackageRef( getPackageName( paths[i] ) ) );
1865 }
1866 }
1867
1868 Packages exportedPkgs = new Packages();
1869 Packages privatePkgs = new Packages();
1870
1871 boolean noprivatePackages = "!*".equals( analyzer.getProperty( Analyzer.PRIVATE_PACKAGE ) );
1872
1873 for ( PackageRef pkg : packages.keySet() )
1874 {
1875
1876 privatePkgs.put( pkg );
1877
1878
1879 String fqn = pkg.getFQN();
1880 if ( noprivatePackages || !( ".".equals( fqn ) || fqn.contains( ".internal" ) || fqn.contains( ".impl" ) ) )
1881 {
1882 exportedPkgs.put( pkg );
1883 }
1884 }
1885
1886 Properties properties = analyzer.getProperties();
1887 String exported = properties.getProperty( Analyzer.EXPORT_PACKAGE );
1888 if ( exported == null )
1889 {
1890 if ( !properties.containsKey( Analyzer.EXPORT_CONTENTS ) )
1891 {
1892
1893 for ( Attrs attrs : exportedPkgs.values() )
1894 {
1895 attrs.put( Constants.SPLIT_PACKAGE_DIRECTIVE, "merge-first" );
1896 }
1897 properties.setProperty( Analyzer.EXPORT_PACKAGE, Processor.printClauses( exportedPkgs ) );
1898 }
1899 else
1900 {
1901
1902 properties.setProperty( Analyzer.EXPORT_PACKAGE, "" );
1903 }
1904 }
1905 else if ( exported.indexOf( LOCAL_PACKAGES ) >= 0 )
1906 {
1907 String newExported = StringUtils.replace( exported, LOCAL_PACKAGES, Processor.printClauses( exportedPkgs ) );
1908 properties.setProperty( Analyzer.EXPORT_PACKAGE, newExported );
1909 }
1910
1911 String internal = properties.getProperty( Analyzer.PRIVATE_PACKAGE );
1912 if ( internal == null )
1913 {
1914 if ( !privatePkgs.isEmpty() )
1915 {
1916 for ( Attrs attrs : privatePkgs.values() )
1917 {
1918 attrs.put( Constants.SPLIT_PACKAGE_DIRECTIVE, "merge-first" );
1919 }
1920 properties.setProperty( Analyzer.PRIVATE_PACKAGE, Processor.printClauses( privatePkgs ) );
1921 }
1922 else
1923 {
1924
1925 properties.setProperty( Analyzer.PRIVATE_PACKAGE, "!*" );
1926 }
1927 }
1928 else if ( internal.indexOf( LOCAL_PACKAGES ) >= 0 )
1929 {
1930 String newInternal = StringUtils.replace( internal, LOCAL_PACKAGES, Processor.printClauses( privatePkgs ) );
1931 properties.setProperty( Analyzer.PRIVATE_PACKAGE, newInternal );
1932 }
1933 }
1934
1935
1936 private static String getPackageName( String filename )
1937 {
1938 int n = filename.lastIndexOf( File.separatorChar );
1939 return n < 0 ? "." : filename.substring( 0, n ).replace( File.separatorChar, '.' );
1940 }
1941
1942
1943 private static List<Resource> getMavenResources( MavenProject currentProject, boolean test )
1944 {
1945 List<Resource> resources = new ArrayList<Resource>( test ? currentProject.getTestResources() : currentProject.getResources() );
1946
1947 if ( currentProject.getCompileSourceRoots() != null )
1948 {
1949
1950 final List<String> packageInfoIncludes = Collections.singletonList( "**/packageinfo" );
1951 for ( Iterator<String> i = currentProject.getCompileSourceRoots().iterator(); i.hasNext(); )
1952 {
1953 String sourceRoot = i.next();
1954 Resource packageInfoResource = new Resource();
1955 packageInfoResource.setDirectory( sourceRoot );
1956 packageInfoResource.setIncludes( packageInfoIncludes );
1957 resources.add( packageInfoResource );
1958 }
1959 }
1960
1961 return resources;
1962 }
1963
1964
1965 protected static String getMavenResourcePaths( MavenProject currentProject, boolean test )
1966 {
1967 final String basePath = currentProject.getBasedir().getAbsolutePath();
1968
1969 Set<String> pathSet = new LinkedHashSet<String>();
1970 for ( Iterator<Resource> i = getMavenResources( currentProject, test ).iterator(); i.hasNext(); )
1971 {
1972 Resource resource = i.next();
1973
1974 final String sourcePath = resource.getDirectory();
1975 final String targetPath = resource.getTargetPath();
1976
1977
1978 if ( new File( sourcePath ).exists() && ( ( targetPath == null ) || ( targetPath.indexOf( ".." ) < 0 ) ) )
1979 {
1980 DirectoryScanner scanner = new DirectoryScanner();
1981
1982 scanner.setBasedir( sourcePath );
1983 if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() )
1984 {
1985 scanner.setIncludes( resource.getIncludes().toArray( EMPTY_STRING_ARRAY ) );
1986 }
1987 else
1988 {
1989 scanner.setIncludes( DEFAULT_INCLUDES );
1990 }
1991
1992 if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() )
1993 {
1994 scanner.setExcludes( resource.getExcludes().toArray( EMPTY_STRING_ARRAY ) );
1995 }
1996
1997 scanner.addDefaultExcludes();
1998 scanner.scan();
1999
2000 List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );
2001
2002 for ( Iterator<String> j = includedFiles.iterator(); j.hasNext(); )
2003 {
2004 String name = j.next();
2005 String path = sourcePath + '/' + name;
2006
2007
2008 if ( path.startsWith( basePath ) )
2009 {
2010 if ( path.length() == basePath.length() )
2011 {
2012 path = ".";
2013 }
2014 else
2015 {
2016 path = path.substring( basePath.length() + 1 );
2017 }
2018 }
2019
2020
2021
2022 if ( File.separatorChar != '/' )
2023 {
2024 name = name.replace( File.separatorChar, '/' );
2025 path = path.replace( File.separatorChar, '/' );
2026 }
2027
2028
2029 path = name + '=' + path;
2030 if ( targetPath != null )
2031 {
2032 path = targetPath + '/' + path;
2033 }
2034
2035
2036 if ( resource.isFiltering() )
2037 {
2038 path = '{' + path + '}';
2039 }
2040
2041 pathSet.add( path );
2042 }
2043 }
2044 }
2045
2046 StringBuffer resourcePaths = new StringBuffer();
2047 for ( Iterator<String> i = pathSet.iterator(); i.hasNext(); )
2048 {
2049 resourcePaths.append( i.next() );
2050 if ( i.hasNext() )
2051 {
2052 resourcePaths.append( ',' );
2053 }
2054 }
2055
2056 return resourcePaths.toString();
2057 }
2058
2059
2060 protected Collection<Artifact> getEmbeddableArtifacts( MavenProject currentProject, DependencyNode dependencyGraph, Analyzer analyzer )
2061 throws MojoExecutionException
2062 {
2063 final Collection<Artifact> artifacts;
2064
2065 String embedTransitive = analyzer.getProperty( DependencyEmbedder.EMBED_TRANSITIVE );
2066 if ( Boolean.valueOf( embedTransitive ).booleanValue() )
2067 {
2068
2069 artifacts = currentProject.getArtifacts();
2070 }
2071 else
2072 {
2073
2074 artifacts = currentProject.getDependencyArtifacts();
2075 }
2076
2077 return getSelectedDependencies( dependencyGraph, artifacts );
2078 }
2079
2080
2081 protected static void addMavenSourcePath( MavenProject currentProject, Analyzer analyzer, Log log )
2082 {
2083
2084 StringBuilder mavenSourcePaths = new StringBuilder();
2085 StringBuilder mavenTestSourcePaths = new StringBuilder();
2086 Map<StringBuilder, List<String>> map = new HashMap<StringBuilder, List<String>>(2);
2087 map.put(mavenSourcePaths, currentProject.getCompileSourceRoots() );
2088 map.put(mavenTestSourcePaths, currentProject.getTestCompileSourceRoots() );
2089 for ( Map.Entry<StringBuilder, List<String>> entry : map.entrySet() )
2090 {
2091 List<String> compileSourceRoots = entry.getValue();
2092 if ( compileSourceRoots != null )
2093 {
2094 StringBuilder sourcePaths = entry.getKey();
2095 for ( Iterator<String> i = compileSourceRoots.iterator(); i.hasNext(); )
2096 {
2097 if ( sourcePaths.length() > 0 )
2098 {
2099 sourcePaths.append( ',' );
2100 }
2101 sourcePaths.append( i.next() );
2102 }
2103 }
2104 }
2105 final String sourcePath = analyzer.getProperty( Analyzer.SOURCEPATH );
2106 if ( sourcePath != null )
2107 {
2108 if ( sourcePath.contains(MAVEN_SOURCES) || sourcePath.contains(MAVEN_TEST_RESOURCES) )
2109 {
2110 String combinedSource = StringUtils.replace( sourcePath, MAVEN_SOURCES, mavenSourcePaths.toString() );
2111 combinedSource = StringUtils.replace( combinedSource, MAVEN_TEST_SOURCES, mavenTestSourcePaths.toString() );
2112 if ( combinedSource.length() > 0 )
2113 {
2114 analyzer.setProperty( Analyzer.SOURCEPATH, combinedSource );
2115 }
2116 else
2117 {
2118 analyzer.unsetProperty( Analyzer.SOURCEPATH );
2119 }
2120 }
2121 else if ( mavenSourcePaths.length() > 0 )
2122 {
2123 log.warn( Analyzer.SOURCEPATH + ": overriding " + mavenSourcePaths + " with " + sourcePath + " (add "
2124 + MAVEN_SOURCES + " if you want to include the maven sources)" );
2125 }
2126 else if ( mavenTestSourcePaths.length() > 0 )
2127 {
2128 log.warn( Analyzer.SOURCEPATH + ": overriding " + mavenTestSourcePaths + " with " + sourcePath + " (add "
2129 + MAVEN_TEST_SOURCES + " if you want to include the maven test sources)" );
2130 }
2131 }
2132 else if ( mavenSourcePaths.length() > 0 )
2133 {
2134 analyzer.setProperty( Analyzer.SOURCEPATH, mavenSourcePaths.toString() );
2135 }
2136 }
2137 }