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