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 @Component
250 protected ArtifactResolver artifactResolver;
251
252
253
254
255
256 @Parameter( readonly = true, required = true, defaultValue = "${localRepository}" )
257 protected ArtifactRepository localRepository;
258
259
260
261
262 @Parameter( readonly = true, required = true, defaultValue = "${project.remoteArtifactRepositories}" )
263 protected List<ArtifactRepository> remoteArtifactRepositories;
264
265
266
267
268
269
270 @Parameter
271 protected List<String> supportedProjectTypes = Arrays.asList("jar", "bundle");
272
273
274
275
276 @Parameter( defaultValue = "${project.build.outputDirectory}" )
277 private File outputDirectory;
278
279
280
281
282 @Parameter( defaultValue = "${project.build.directory}" )
283 private String buildDirectory;
284
285
286
287
288 @Parameter( defaultValue = "${project}", readonly = true, required = true )
289 protected MavenProject project;
290
291
292
293
294
295
296
297 @Parameter
298 private Map<String, String> instructions = new LinkedHashMap<String, String>();
299
300
301
302
303 private final Maven2OsgiConverter m_maven2OsgiConverter = new DefaultMaven2OsgiConverter();
304
305
306
307
308 @Parameter
309 private MavenArchiveConfiguration archive;
310
311 @Parameter( defaultValue = "${session}", readonly = true, required = true )
312 private MavenSession m_mavenSession;
313
314 @Component
315 protected BuildContext buildContext;
316
317 private static final String MAVEN_SYMBOLICNAME = "maven-symbolicname";
318 private static final String MAVEN_RESOURCES = "{maven-resources}";
319 private static final String MAVEN_TEST_RESOURCES = "{maven-test-resources}";
320 private static final String LOCAL_PACKAGES = "{local-packages}";
321 private static final String MAVEN_SOURCES = "{maven-sources}";
322 private static final String MAVEN_TEST_SOURCES = "{maven-test-sources}";
323 private static final String BUNDLE_PLUGIN_EXTENSION = "BNDExtension-";
324 private static final String BUNDLE_PLUGIN_PREPEND_EXTENSION = "BNDPrependExtension-";
325
326 private static final String[] EMPTY_STRING_ARRAY =
327 {};
328 private static final String[] DEFAULT_INCLUDES =
329 { "**/**" };
330
331 private static final String NL = System.getProperty( "line.separator" );
332
333
334 protected Maven2OsgiConverter getMaven2OsgiConverter()
335 {
336 return m_maven2OsgiConverter;
337 }
338
339
340 protected MavenProject getProject()
341 {
342 return project;
343 }
344
345 protected DependencyNode buildDependencyGraph( MavenProject mavenProject ) throws MojoExecutionException
346 {
347 DependencyNode dependencyGraph;
348 try
349 {
350 dependencyGraph = m_dependencyGraphBuilder.buildDependencyGraph( mavenProject, null );
351 }
352 catch ( DependencyGraphBuilderException e )
353 {
354 throw new MojoExecutionException( e.getMessage(), e );
355 }
356 return dependencyGraph;
357 }
358
359
360
361
362 public void execute() throws MojoExecutionException
363 {
364 Properties properties = new Properties();
365 String projectType = getProject().getArtifact().getType();
366
367
368 if ( !supportedProjectTypes.contains( projectType ) )
369 {
370 getLog().warn(
371 "Ignoring project type " + projectType + " - supportedProjectTypes = " + supportedProjectTypes );
372 return;
373 }
374
375 execute( getProject(), buildDependencyGraph(getProject()), instructions, properties );
376 }
377
378
379 protected void execute( MavenProject currentProject, DependencyNode dependencyGraph, Map<String, String> originalInstructions, Properties properties )
380 throws MojoExecutionException
381 {
382 try
383 {
384 execute( currentProject, dependencyGraph, originalInstructions, properties, getClasspath( currentProject, dependencyGraph ) );
385 }
386 catch ( IOException e )
387 {
388 throw new MojoExecutionException( "Error calculating classpath for project " + currentProject, e );
389 }
390 }
391
392
393
394 protected static Map<String, String> transformDirectives( Map<String, String> originalInstructions )
395 {
396 Map<String, String> transformedInstructions = new LinkedHashMap<String, String>();
397 for ( Iterator<Map.Entry<String, String>> i = originalInstructions.entrySet().iterator(); i.hasNext(); )
398 {
399 Map.Entry<String, String> e = i.next();
400
401 String key = e.getKey();
402 if ( key.startsWith( "_" ) )
403 {
404 key = "-" + key.substring( 1 );
405 }
406
407 String value = e.getValue();
408 if ( null == value )
409 {
410 value = "";
411 }
412 else
413 {
414 value = value.replaceAll( "\\p{Blank}*[\r\n]\\p{Blank}*", "" );
415 }
416
417 if ( Analyzer.WAB.equals( key ) && value.length() == 0 )
418 {
419
420 value = "src/main/webapp/";
421 }
422
423 transformedInstructions.put( key, value );
424 }
425 return transformedInstructions;
426 }
427
428
429 protected boolean reportErrors( String prefix, Analyzer analyzer )
430 {
431 List<String> errors = analyzer.getErrors();
432 List<String> warnings = analyzer.getWarnings();
433
434 for ( Iterator<String> w = warnings.iterator(); w.hasNext(); )
435 {
436 String msg = w.next();
437 getLog().warn( prefix + " : " + msg );
438 }
439
440 boolean hasErrors = false;
441 String fileNotFound = "Input file does not exist: ";
442 for ( Iterator<String> e = errors.iterator(); e.hasNext(); )
443 {
444 String msg = e.next();
445 if ( msg.startsWith(fileNotFound) && msg.endsWith( "~" ) )
446 {
447
448 String duplicate = Processor.removeDuplicateMarker( msg.substring( fileNotFound.length() ) );
449 getLog().warn( prefix + " : Duplicate path '" + duplicate + "' in Include-Resource" );
450 }
451 else
452 {
453 getLog().error( prefix + " : " + msg );
454 hasErrors = true;
455 }
456 }
457 return hasErrors;
458 }
459
460
461 protected void execute( MavenProject currentProject, DependencyNode dependencyGraph, Map<String, String> originalInstructions, Properties properties,
462 Jar[] classpath ) throws MojoExecutionException
463 {
464 try
465 {
466 File jarFile = new File( getBuildDirectory(), getBundleName( currentProject ) );
467 Builder builder = buildOSGiBundle( currentProject, dependencyGraph, originalInstructions, properties, classpath );
468 boolean hasErrors = reportErrors( "Bundle " + currentProject.getArtifact(), builder );
469 if ( hasErrors )
470 {
471 String failok = builder.getProperty( "-failok" );
472 if ( null == failok || "false".equalsIgnoreCase( failok ) )
473 {
474 jarFile.delete();
475
476 throw new MojoFailureException( "Error(s) found in bundle configuration" );
477 }
478 }
479
480
481 jarFile.getParentFile().mkdirs();
482 builder.getJar().write( jarFile );
483
484 Artifact mainArtifact = currentProject.getArtifact();
485
486 if ( "bundle".equals( mainArtifact.getType() ) )
487 {
488
489 mainArtifact.setArtifactHandler( m_artifactHandlerManager.getArtifactHandler( "jar" ) );
490 }
491
492 boolean customClassifier = null != classifier && classifier.trim().length() > 0;
493 boolean customPackaging = null != packaging && packaging.trim().length() > 0;
494
495 if ( customClassifier && customPackaging )
496 {
497 m_projectHelper.attachArtifact( currentProject, packaging, classifier, jarFile );
498 }
499 else if ( customClassifier )
500 {
501 m_projectHelper.attachArtifact( currentProject, jarFile, classifier );
502 }
503 else if ( customPackaging )
504 {
505 m_projectHelper.attachArtifact( currentProject, packaging, jarFile );
506 }
507 else
508 {
509 mainArtifact.setFile( jarFile );
510 }
511
512 if ( unpackBundle )
513 {
514 unpackBundle( jarFile );
515 }
516
517 if ( manifestLocation != null )
518 {
519 File outputFile = new File( manifestLocation, "MANIFEST.MF" );
520
521 try
522 {
523 ManifestPlugin.writeManifest( builder, outputFile, niceManifest, exportScr, scrLocation, buildContext, getLog() );
524 }
525 catch ( IOException e )
526 {
527 getLog().error( "Error trying to write Manifest to file " + outputFile, e );
528 }
529 }
530
531
532 builder.close();
533 }
534 catch ( MojoFailureException e )
535 {
536 getLog().error( e.getLocalizedMessage() );
537 throw new MojoExecutionException( "Error(s) found in bundle configuration", e );
538 }
539 catch ( Exception e )
540 {
541 getLog().error( "An internal error occurred", e );
542 throw new MojoExecutionException( "Internal error in maven-bundle-plugin", e );
543 }
544 }
545
546
547 protected Builder getOSGiBuilder( MavenProject currentProject, Map<String, String> originalInstructions, Properties properties,
548 Jar[] classpath ) throws Exception
549 {
550 properties.putAll( getDefaultProperties( currentProject ) );
551 properties.putAll( transformDirectives( originalInstructions ) );
552
553
554 final Map<String, String> addProps = new HashMap<String, String>();
555 final Iterator<Map.Entry<Object, Object>> iter = currentProject.getProperties().entrySet().iterator();
556 while ( iter.hasNext() )
557 {
558 final Map.Entry<Object, Object> entry = iter.next();
559 final String key = entry.getKey().toString();
560 if ( key.startsWith(BUNDLE_PLUGIN_EXTENSION) )
561 {
562 final String oKey = key.substring(BUNDLE_PLUGIN_EXTENSION.length());
563 final String currentValue = properties.getProperty(oKey);
564 if ( currentValue == null )
565 {
566 addProps.put(oKey, entry.getValue().toString());
567 }
568 else
569 {
570 addProps.put(oKey, currentValue + ',' + entry.getValue());
571 }
572 }
573 if ( key.startsWith(BUNDLE_PLUGIN_PREPEND_EXTENSION) )
574 {
575 final String oKey = key.substring(BUNDLE_PLUGIN_PREPEND_EXTENSION.length());
576 final String currentValue = properties.getProperty(oKey);
577 if ( currentValue == null )
578 {
579 addProps.put(oKey, entry.getValue().toString());
580 }
581 else
582 {
583 addProps.put(oKey, entry.getValue() + "," + currentValue);
584 }
585 }
586 }
587 properties.putAll( addProps );
588 final Iterator<String> keyIter = addProps.keySet().iterator();
589 while ( keyIter.hasNext() )
590 {
591 Object key = keyIter.next();
592 properties.remove(BUNDLE_PLUGIN_EXTENSION + key);
593 properties.remove(BUNDLE_PLUGIN_PREPEND_EXTENSION + key);
594 }
595
596 if (properties.getProperty("Bundle-Activator") != null
597 && properties.getProperty("Bundle-Activator").isEmpty())
598 {
599 properties.remove("Bundle-Activator");
600 }
601 if (properties.containsKey("-disable-plugin"))
602 {
603 String[] disabled = properties.remove("-disable-plugin").toString().replaceAll(" ", "").split(",");
604 String[] enabled = properties.getProperty(Analyzer.PLUGIN, "").replaceAll(" ", "").split(",");
605 Set<String> plugin = new LinkedHashSet<String>();
606 plugin.addAll(Arrays.asList(enabled));
607 plugin.removeAll(Arrays.asList(disabled));
608 StringBuilder sb = new StringBuilder();
609 for (String s : plugin)
610 {
611 if (sb.length() > 0)
612 {
613 sb.append(",");
614 }
615 sb.append(s);
616 }
617 properties.setProperty(Analyzer.PLUGIN, sb.toString());
618 }
619
620 Builder builder = new Builder();
621 synchronized ( BundlePlugin.class )
622 {
623 builder.setBase( getBase( currentProject ) );
624 }
625 builder.setProperties( sanitize( properties ) );
626 if ( classpath != null )
627 {
628 builder.setClasspath( classpath );
629 }
630
631 return builder;
632 }
633
634
635 protected static Properties sanitize( Properties properties )
636 {
637
638 Properties sanitizedEntries = new Properties();
639 for ( Iterator<Map.Entry<Object,Object>> itr = properties.entrySet().iterator(); itr.hasNext(); )
640 {
641 Map.Entry<Object,Object> entry = itr.next();
642 if ( entry.getKey() instanceof String == false )
643 {
644 String key = sanitize(entry.getKey());
645 if ( !properties.containsKey( key ) )
646 {
647 sanitizedEntries.setProperty( key, sanitize( entry.getValue() ) );
648 }
649 itr.remove();
650 }
651 else if ( entry.getValue() instanceof String == false )
652 {
653 entry.setValue( sanitize( entry.getValue() ) );
654 }
655 }
656 properties.putAll( sanitizedEntries );
657 return properties;
658 }
659
660
661 protected static String sanitize( Object value )
662 {
663 if ( value instanceof String )
664 {
665 return ( String ) value;
666 }
667 else if ( value instanceof Iterable )
668 {
669 String delim = "";
670 StringBuilder buf = new StringBuilder();
671 for ( Object i : ( Iterable<?> ) value )
672 {
673 buf.append( delim ).append( i );
674 delim = ", ";
675 }
676 return buf.toString();
677 }
678 else if ( value.getClass().isArray() )
679 {
680 String delim = "";
681 StringBuilder buf = new StringBuilder();
682 for ( int i = 0, len = Array.getLength( value ); i < len; i++ )
683 {
684 buf.append( delim ).append( Array.get( value, i ) );
685 delim = ", ";
686 }
687 return buf.toString();
688 }
689 else
690 {
691 return String.valueOf( value );
692 }
693 }
694
695
696 protected void addMavenInstructions( MavenProject currentProject, DependencyNode dependencyGraph, Builder builder ) throws Exception
697 {
698 if ( currentProject.getBasedir() != null )
699 {
700
701 includeMavenResources(currentProject, builder, getLog());
702
703
704 includeJava9Fixups(currentProject, builder);
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;
1192 try (Analyzer analyzer = new Analyzer()) {
1193 values = analyzer.parseHeader( importPackages );
1194 }
1195 for ( Map.Entry<String, ? extends Map<String, String>> entry : values.entrySet() )
1196 {
1197 String pkg = entry.getKey();
1198 Map<String, String> options = entry.getValue();
1199 if ( !options.containsKey( "resolution:" ) && optionalPackages.contains( pkg ) )
1200 {
1201 options.put( "resolution:", "optional" );
1202 }
1203 }
1204 String result = Processor.printClauses( values );
1205 bundleManifest.getMainAttributes().putValue( "Import-Package", result );
1206 }
1207
1208 jar.setManifest( bundleManifest );
1209 }
1210 catch ( Exception e )
1211 {
1212 getLog().warn( "Unable to merge Maven manifest: " + e.getLocalizedMessage() );
1213 }
1214
1215 if ( addMavenDescriptor )
1216 {
1217 doMavenMetadata( currentProject, jar );
1218 }
1219
1220 if ( getLog().isDebugEnabled() )
1221 {
1222 getLog().debug( "Final Manifest:" + NL + dumpManifest( jar.getManifest(), new StringBuilder() ) );
1223 }
1224
1225 builder.setJar( jar );
1226 }
1227
1228
1229 protected static void mergeManifest( Instructions instructions, Manifest... manifests ) throws IOException
1230 {
1231 for ( int i = manifests.length - 2; i >= 0; i-- )
1232 {
1233 Manifest mergedManifest = manifests[i];
1234 Manifest manifest = manifests[i + 1];
1235 Attributes mergedMainAttributes = mergedManifest.getMainAttributes();
1236 Attributes mainAttributes = manifest.getMainAttributes();
1237 Attributes filteredMainAttributes = filterAttributes( instructions, mainAttributes, null );
1238 if ( !filteredMainAttributes.isEmpty() )
1239 {
1240 mergeAttributes( mergedMainAttributes, filteredMainAttributes );
1241 }
1242 Map<String, Attributes> mergedEntries = mergedManifest.getEntries();
1243 Map<String, Attributes> entries = manifest.getEntries();
1244 for ( Map.Entry<String, Attributes> entry : entries.entrySet() )
1245 {
1246 String name = entry.getKey();
1247 Attributes attributes = entry.getValue();
1248 Attributes filteredAttributes = filterAttributes( instructions, attributes, null );
1249 if ( !filteredAttributes.isEmpty() )
1250 {
1251 Attributes mergedAttributes = mergedManifest.getAttributes( name );
1252 if ( mergedAttributes != null)
1253 {
1254 mergeAttributes(mergedAttributes, filteredAttributes);
1255 }
1256 else
1257 {
1258 mergedEntries.put(name, filteredAttributes);
1259 }
1260 }
1261 }
1262 }
1263 }
1264
1265
1266
1267
1268
1269 private static Attributes filterAttributes(Instructions instructions, Attributes source, Set<Instruction> nomatch) {
1270 Attributes result = new Attributes();
1271 Map<String, Object> keys = new TreeMap<String, Object>();
1272 for ( Object key : source.keySet() )
1273 {
1274 keys.put( key.toString(), key );
1275 }
1276
1277 List<Instruction> filters = new ArrayList<Instruction>( instructions.keySet() );
1278 if (nomatch == null)
1279 {
1280 nomatch = Create.set();
1281 }
1282 for ( Instruction instruction : filters ) {
1283 boolean match = false;
1284 for (Iterator<Map.Entry<String, Object>> i = keys.entrySet().iterator(); i.hasNext();)
1285 {
1286 Map.Entry<String, Object> entry = i.next();
1287 String key = entry.getKey();
1288 if ( instruction.matches( key ) )
1289 {
1290 match = true;
1291 if (!instruction.isNegated()) {
1292 Object name = entry.getValue();
1293 Object value = source.get( name );
1294 result.put( name, value );
1295 }
1296 i.remove();
1297 }
1298 }
1299 if (!match && !instruction.isAny())
1300 nomatch.add(instruction);
1301 }
1302
1303
1304
1305
1306
1307
1308
1309
1310 for (Iterator<Instruction> i = nomatch.iterator(); i.hasNext();) {
1311 Instruction instruction = i.next();
1312
1313
1314
1315
1316
1317 if (instruction.isLiteral() && !instruction.isNegated()) {
1318 Object key = keys.get( instruction.getLiteral() );
1319 if ( key != null )
1320 {
1321 Object value = source.get( key );
1322 result.put( key, value );
1323 }
1324 i.remove();
1325 continue;
1326 }
1327
1328
1329
1330
1331
1332 if (instruction.isNegated()) {
1333 i.remove();
1334 continue;
1335 }
1336
1337
1338
1339 if (instruction.isOptional()) {
1340 i.remove();
1341 continue;
1342 }
1343 }
1344 return result;
1345 }
1346
1347
1348 private static void mergeAttributes( Attributes... attributesArray ) throws IOException
1349 {
1350 for ( int i = attributesArray.length - 2; i >= 0; i-- )
1351 {
1352 Attributes mergedAttributes = attributesArray[i];
1353 Attributes attributes = attributesArray[i + 1];
1354 for ( Map.Entry<Object, Object> entry : attributes.entrySet() )
1355 {
1356 Object name = entry.getKey();
1357 String value = (String) entry.getValue();
1358 String oldValue = (String) mergedAttributes.put( name, value );
1359 if ( oldValue != null )
1360 {
1361 Parameters mergedClauses = OSGiHeader.parseHeader(oldValue);
1362 Parameters clauses = OSGiHeader.parseHeader( value );
1363 if ( !mergedClauses.isEqual( clauses) )
1364 {
1365 for ( Map.Entry<String, Attrs> clauseEntry : clauses.entrySet() )
1366 {
1367 String clause = clauseEntry.getKey();
1368 Attrs attrs = clauseEntry.getValue();
1369 Attrs mergedAttrs = mergedClauses.get( clause );
1370 if ( mergedAttrs == null)
1371 {
1372 mergedClauses.put( clause, attrs );
1373 }
1374 else if ( !mergedAttrs.isEqual(attrs) )
1375 {
1376 for ( Map.Entry<String,String> adentry : attrs.entrySet() )
1377 {
1378 String adname = adentry.getKey();
1379 String ad = adentry.getValue();
1380 if ( mergedAttrs.containsKey( adname ) )
1381 {
1382 Attrs.Type type = attrs.getType( adname );
1383 switch (type)
1384 {
1385 case VERSIONS:
1386 case STRINGS:
1387 case LONGS:
1388 case DOUBLES:
1389 ExtList<String> mergedAd = ExtList.from( mergedAttrs.get( adname ) );
1390 ExtList.from( ad ).addAll( ExtList.from( ad ) );
1391 mergedAttrs.put(adname, mergedAd.join() );
1392 break;
1393 }
1394 }
1395 else
1396 {
1397 mergedAttrs.put( adname, ad );
1398 }
1399 }
1400 }
1401 }
1402 mergedAttributes.put( name, Processor.printClauses( mergedClauses ) );
1403 }
1404 }
1405 }
1406 }
1407 }
1408
1409
1410 protected Set<String> getOptionalPackages( MavenProject currentProject, DependencyNode dependencyGraph ) throws IOException, MojoExecutionException
1411 {
1412 ArrayList<Artifact> inscope = new ArrayList<Artifact>();
1413 final Collection<Artifact> artifacts = getSelectedDependencies( dependencyGraph, currentProject.getArtifacts() );
1414 for ( Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); )
1415 {
1416 Artifact artifact = it.next();
1417 if ( artifact.getArtifactHandler().isAddedToClasspath() )
1418 {
1419 inscope.add( artifact );
1420 }
1421 }
1422
1423 HashSet<String> optionalArtifactIds = new HashSet<String>();
1424 for ( Iterator<Artifact> it = inscope.iterator(); it.hasNext(); )
1425 {
1426 Artifact artifact = it.next();
1427 if ( artifact.isOptional() )
1428 {
1429 String id = artifact.toString();
1430 if ( artifact.getScope() != null )
1431 {
1432
1433 id = id.replaceFirst( ":[^:]*$", "" );
1434 }
1435 optionalArtifactIds.add( id );
1436 }
1437
1438 }
1439
1440 HashSet<String> required = new HashSet<String>();
1441 HashSet<String> optional = new HashSet<String>();
1442 for ( Iterator<Artifact> it = inscope.iterator(); it.hasNext(); )
1443 {
1444 Artifact artifact = it.next();
1445 File file = getFile( artifact );
1446 if ( file == null )
1447 {
1448 continue;
1449 }
1450
1451 Jar jar = new Jar( artifact.getArtifactId(), file );
1452 if ( isTransitivelyOptional( optionalArtifactIds, artifact ) )
1453 {
1454 optional.addAll( jar.getPackages() );
1455 }
1456 else
1457 {
1458 required.addAll( jar.getPackages() );
1459 }
1460 jar.close();
1461 }
1462
1463 optional.removeAll( required );
1464 return optional;
1465 }
1466
1467
1468
1469
1470
1471
1472
1473
1474 protected boolean isTransitivelyOptional( HashSet<String> optionalArtifactIds, Artifact artifact )
1475 {
1476 List<String> trail = artifact.getDependencyTrail();
1477 for ( Iterator<String> iterator = trail.iterator(); iterator.hasNext(); )
1478 {
1479 String next = iterator.next();
1480 if ( optionalArtifactIds.contains( next ) )
1481 {
1482 return true;
1483 }
1484 }
1485 return false;
1486 }
1487
1488
1489 private void unpackBundle( File jarFile )
1490 {
1491 File outputDir = getOutputDirectory();
1492 if ( null == outputDir )
1493 {
1494 outputDir = new File( getBuildDirectory(), "classes" );
1495 }
1496
1497 try
1498 {
1499
1500
1501
1502
1503 if ( !outputDir.exists() )
1504 {
1505 outputDir.mkdirs();
1506 }
1507
1508 UnArchiver unArchiver = m_archiverManager.getUnArchiver( "jar" );
1509 unArchiver.setDestDirectory( outputDir );
1510 unArchiver.setSourceFile( jarFile );
1511 unArchiver.extract();
1512 }
1513 catch ( Exception e )
1514 {
1515 getLog().error( "Problem unpacking " + jarFile + " to " + outputDir, e );
1516 }
1517 }
1518
1519
1520 protected static String removeTagFromInstruction( String instruction, String tag )
1521 {
1522 StringBuffer buf = new StringBuffer();
1523
1524 String[] clauses = instruction.split( "," );
1525 for ( int i = 0; i < clauses.length; i++ )
1526 {
1527 String clause = clauses[i].trim();
1528 if ( !tag.equals( clause ) )
1529 {
1530 if ( buf.length() > 0 )
1531 {
1532 buf.append( ',' );
1533 }
1534 buf.append( clause );
1535 }
1536 }
1537
1538 return buf.toString();
1539 }
1540
1541
1542 private static Map<String, String> getProperties( Model projectModel, String prefix )
1543 {
1544 Map<String, String> properties = new LinkedHashMap<String, String>();
1545 Method methods[] = Model.class.getDeclaredMethods();
1546 for ( int i = 0; i < methods.length; i++ )
1547 {
1548 String name = methods[i].getName();
1549 if ( name.startsWith( "get" ) )
1550 {
1551 try
1552 {
1553 Object v = methods[i].invoke( projectModel, null );
1554 if ( v != null )
1555 {
1556 name = prefix + Character.toLowerCase( name.charAt( 3 ) ) + name.substring( 4 );
1557 if ( v.getClass().isArray() )
1558 properties.put( name, Arrays.asList( ( Object[] ) v ).toString() );
1559 else
1560 properties.put( name, v.toString() );
1561
1562 }
1563 }
1564 catch ( Exception e )
1565 {
1566
1567 }
1568 }
1569 }
1570 return properties;
1571 }
1572
1573
1574 private static StringBuffer printLicenses( List<License> licenses )
1575 {
1576 if ( licenses == null || licenses.size() == 0 )
1577 return null;
1578 StringBuffer sb = new StringBuffer();
1579 String del = "";
1580 for ( Iterator<License> i = licenses.iterator(); i.hasNext(); )
1581 {
1582 License l = i.next();
1583 String url = l.getUrl();
1584 if ( url == null )
1585 continue;
1586 sb.append( del );
1587 sb.append( url );
1588 del = ", ";
1589 }
1590 if ( sb.length() == 0 )
1591 return null;
1592 return sb;
1593 }
1594
1595
1596
1597
1598
1599
1600 private void doMavenMetadata( MavenProject currentProject, Jar jar ) throws IOException
1601 {
1602 String path = "META-INF/maven/" + currentProject.getGroupId() + "/" + currentProject.getArtifactId();
1603
1604 File pomFile = currentProject.getFile();
1605 if ( pomFile == null || !pomFile.exists() )
1606 {
1607 pomFile = new File( currentProject.getBasedir(), "pom.xml" );
1608 }
1609 if ( pomFile.exists() )
1610 {
1611 jar.putResource( path + "/pom.xml", new FileResource( pomFile ) );
1612 }
1613
1614 Properties p = new Properties();
1615 p.put( "version", currentProject.getVersion() );
1616 p.put( "groupId", currentProject.getGroupId() );
1617 p.put( "artifactId", currentProject.getArtifactId() );
1618 ByteArrayOutputStream out = new ByteArrayOutputStream();
1619 p.store( out, "Generated by org.apache.felix.bundleplugin" );
1620 jar.putResource( path + "/pom.properties", new EmbeddedResource( out.toByteArray(), System.currentTimeMillis() ) );
1621 }
1622
1623
1624 protected Jar[] getClasspath( MavenProject currentProject, DependencyNode dependencyGraph ) throws IOException, MojoExecutionException
1625 {
1626 List<Jar> list = new ArrayList<Jar>();
1627
1628 if ( getOutputDirectory() != null && getOutputDirectory().exists() )
1629 {
1630 list.add( new Jar( ".", getOutputDirectory() ) );
1631 }
1632
1633 final Collection<Artifact> artifacts = getSelectedDependencies( dependencyGraph, currentProject.getArtifacts() );
1634 for ( Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); )
1635 {
1636 Artifact artifact = it.next();
1637 if ( artifact.getArtifactHandler().isAddedToClasspath() && !Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
1638 {
1639 File file = getFile( artifact );
1640 if ( file == null )
1641 {
1642 getLog().warn(
1643 "File is not available for artifact " + artifact + " in project "
1644 + currentProject.getArtifact() );
1645 continue;
1646 }
1647 Jar jar = new Jar( artifact.getArtifactId(), file );
1648 list.add( jar );
1649 }
1650 }
1651 Jar[] cp = new Jar[list.size()];
1652 list.toArray( cp );
1653 return cp;
1654 }
1655
1656
1657 private Collection<Artifact> getSelectedDependencies( DependencyNode dependencyGraph, Collection<Artifact> artifacts ) throws MojoExecutionException
1658 {
1659 if ( null == excludeDependencies || excludeDependencies.length() == 0 )
1660 {
1661 return artifacts;
1662 }
1663 else if ( "true".equalsIgnoreCase( excludeDependencies ) )
1664 {
1665 return Collections.emptyList();
1666 }
1667
1668 Collection<Artifact> selectedDependencies = new LinkedHashSet<Artifact>( artifacts );
1669 DependencyExcluder excluder = new DependencyExcluder( dependencyGraph, artifacts );
1670 excluder.processHeaders( excludeDependencies );
1671 selectedDependencies.removeAll( excluder.getExcludedArtifacts() );
1672
1673 return selectedDependencies;
1674 }
1675
1676
1677
1678
1679
1680
1681
1682 protected File getFile( Artifact artifact )
1683 {
1684 return artifact.getFile();
1685 }
1686
1687
1688 private static void header( Properties properties, String key, Object value )
1689 {
1690 if ( value == null )
1691 return;
1692
1693 if ( value instanceof Collection && ( ( Collection ) value ).isEmpty() )
1694 return;
1695
1696 properties.put( key, value.toString().replaceAll( "[\r\n]", "" ) );
1697 }
1698
1699
1700
1701
1702
1703
1704
1705
1706 protected String convertVersionToOsgi( String version )
1707 {
1708 return getMaven2OsgiConverter().getVersion( version );
1709 }
1710
1711
1712
1713
1714
1715 protected String getBundleName( MavenProject currentProject )
1716 {
1717 String extension;
1718 try
1719 {
1720 extension = currentProject.getArtifact().getArtifactHandler().getExtension();
1721 }
1722 catch ( Throwable e )
1723 {
1724 extension = currentProject.getArtifact().getType();
1725 }
1726 if ( StringUtils.isEmpty( extension ) || "bundle".equals( extension ) || "pom".equals( extension ) )
1727 {
1728 extension = "jar";
1729 }
1730 if ( null != classifier && classifier.trim().length() > 0 )
1731 {
1732 return finalName + '-' + classifier + '.' + extension;
1733 }
1734 return finalName + '.' + extension;
1735 }
1736
1737
1738 protected String getBuildDirectory()
1739 {
1740 return buildDirectory;
1741 }
1742
1743
1744 protected void setBuildDirectory( String _buildirectory )
1745 {
1746 buildDirectory = _buildirectory;
1747 }
1748
1749
1750 protected Properties getDefaultProperties( MavenProject currentProject )
1751 {
1752 Properties properties = new Properties();
1753
1754 String bsn;
1755 try
1756 {
1757 bsn = getMaven2OsgiConverter().getBundleSymbolicName( currentProject.getArtifact() );
1758 }
1759 catch ( Exception e )
1760 {
1761 bsn = currentProject.getGroupId() + "." + currentProject.getArtifactId();
1762 }
1763
1764
1765 properties.put( MAVEN_SYMBOLICNAME, bsn );
1766 properties.put( Analyzer.BUNDLE_SYMBOLICNAME, bsn );
1767 properties.put( Analyzer.IMPORT_PACKAGE, "*" );
1768 properties.put( Analyzer.BUNDLE_VERSION, getMaven2OsgiConverter().getVersion( currentProject.getVersion() ) );
1769
1770
1771 properties.put( Constants.REMOVEHEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE );
1772
1773 header( properties, Analyzer.BUNDLE_DESCRIPTION, currentProject.getDescription() );
1774 StringBuffer licenseText = printLicenses( currentProject.getLicenses() );
1775 if ( licenseText != null )
1776 {
1777 header( properties, Analyzer.BUNDLE_LICENSE, licenseText );
1778 }
1779 header( properties, Analyzer.BUNDLE_NAME, currentProject.getName() );
1780
1781 if ( currentProject.getOrganization() != null )
1782 {
1783 if ( currentProject.getOrganization().getName() != null )
1784 {
1785 String organizationName = currentProject.getOrganization().getName();
1786 header( properties, Analyzer.BUNDLE_VENDOR, organizationName );
1787 properties.put( "project.organization.name", organizationName );
1788 properties.put( "pom.organization.name", organizationName );
1789 }
1790 if ( currentProject.getOrganization().getUrl() != null )
1791 {
1792 String organizationUrl = currentProject.getOrganization().getUrl();
1793 header( properties, Analyzer.BUNDLE_DOCURL, organizationUrl );
1794 properties.put( "project.organization.url", organizationUrl );
1795 properties.put( "pom.organization.url", organizationUrl );
1796 }
1797 }
1798
1799 properties.putAll( currentProject.getProperties() );
1800 properties.putAll( currentProject.getModel().getProperties() );
1801
1802 for ( Iterator<String> i = currentProject.getFilters().iterator(); i.hasNext(); )
1803 {
1804 File filterFile = new File( i.next() );
1805 if ( filterFile.isFile() )
1806 {
1807 properties.putAll( PropertyUtils.loadProperties( filterFile ) );
1808 }
1809 }
1810
1811 if ( m_mavenSession != null )
1812 {
1813 try
1814 {
1815
1816 Properties sessionProperties = m_mavenSession.getExecutionProperties();
1817 for ( Enumeration<String> e = (Enumeration<String>) sessionProperties.propertyNames(); e.hasMoreElements(); )
1818 {
1819 String key = e.nextElement();
1820 if ( key.length() > 0 && !Character.isUpperCase( key.charAt( 0 ) ) )
1821 {
1822 properties.put( key, sessionProperties.getProperty( key ) );
1823 }
1824 }
1825 }
1826 catch ( Exception e )
1827 {
1828 getLog().warn( "Problem with Maven session properties: " + e.getLocalizedMessage() );
1829 }
1830 }
1831
1832 properties.putAll( getProperties( currentProject.getModel(), "project.build." ) );
1833 properties.putAll( getProperties( currentProject.getModel(), "pom." ) );
1834 properties.putAll( getProperties( currentProject.getModel(), "project." ) );
1835
1836 properties.put( "project.baseDir", getBase( currentProject ) );
1837 properties.put( "project.build.directory", getBuildDirectory() );
1838 properties.put( "project.build.outputdirectory", getOutputDirectory() );
1839
1840 properties.put( "classifier", classifier == null ? "" : classifier );
1841
1842
1843 header( properties, Analyzer.PLUGIN, BlueprintPlugin.class.getName() + ","
1844 + SpringXMLType.class.getName() + ","
1845 + JpaPlugin.class.getName() );
1846
1847 return properties;
1848 }
1849
1850
1851 protected static File getBase( MavenProject currentProject )
1852 {
1853 return currentProject.getBasedir() != null ? currentProject.getBasedir() : new File( "" );
1854 }
1855
1856
1857 protected File getOutputDirectory()
1858 {
1859 return outputDirectory;
1860 }
1861
1862
1863 protected void setOutputDirectory( File _outputDirectory )
1864 {
1865 outputDirectory = _outputDirectory;
1866 }
1867
1868
1869 private static void addLocalPackages( File outputDirectory, Analyzer analyzer ) throws IOException
1870 {
1871 Packages packages = new Packages();
1872
1873 if ( outputDirectory != null && outputDirectory.isDirectory() )
1874 {
1875
1876 DirectoryScanner scanner = new DirectoryScanner();
1877 scanner.setBasedir( outputDirectory );
1878 scanner.setIncludes( new String[]
1879 { "**/*.class" } );
1880
1881 scanner.addDefaultExcludes();
1882 scanner.scan();
1883
1884 String[] paths = scanner.getIncludedFiles();
1885 for ( int i = 0; i < paths.length; i++ )
1886 {
1887 packages.put( analyzer.getPackageRef( getPackageName( paths[i] ) ) );
1888 }
1889 }
1890
1891 Packages exportedPkgs = new Packages();
1892 Packages privatePkgs = new Packages();
1893
1894 boolean noprivatePackages = "!*".equals( analyzer.getProperty( Analyzer.PRIVATE_PACKAGE ) );
1895
1896 for ( PackageRef pkg : packages.keySet() )
1897 {
1898
1899 privatePkgs.put( pkg );
1900
1901
1902 String fqn = pkg.getFQN();
1903 if ( noprivatePackages || !( ".".equals( fqn ) || fqn.contains( ".internal" ) || fqn.contains( ".impl" ) ) )
1904 {
1905 exportedPkgs.put( pkg );
1906 }
1907 }
1908
1909 Properties properties = analyzer.getProperties();
1910 String exported = properties.getProperty( Analyzer.EXPORT_PACKAGE );
1911 if ( exported == null )
1912 {
1913 if ( !properties.containsKey( Analyzer.EXPORT_CONTENTS ) )
1914 {
1915
1916 for ( Attrs attrs : exportedPkgs.values() )
1917 {
1918 attrs.put( Constants.SPLIT_PACKAGE_DIRECTIVE, "merge-first" );
1919 }
1920 properties.setProperty( Analyzer.EXPORT_PACKAGE, Processor.printClauses( exportedPkgs ) );
1921 }
1922 else
1923 {
1924
1925 properties.setProperty( Analyzer.EXPORT_PACKAGE, "" );
1926 }
1927 }
1928 else if ( exported.indexOf( LOCAL_PACKAGES ) >= 0 )
1929 {
1930 String newExported = StringUtils.replace( exported, LOCAL_PACKAGES, Processor.printClauses( exportedPkgs ) );
1931 properties.setProperty( Analyzer.EXPORT_PACKAGE, newExported );
1932 }
1933
1934 String internal = properties.getProperty( Analyzer.PRIVATE_PACKAGE );
1935 if ( internal == null )
1936 {
1937 if ( !privatePkgs.isEmpty() )
1938 {
1939 for ( Attrs attrs : privatePkgs.values() )
1940 {
1941 attrs.put( Constants.SPLIT_PACKAGE_DIRECTIVE, "merge-first" );
1942 }
1943 properties.setProperty( Analyzer.PRIVATE_PACKAGE, Processor.printClauses( privatePkgs ) );
1944 }
1945 else
1946 {
1947
1948 properties.setProperty( Analyzer.PRIVATE_PACKAGE, "!*" );
1949 }
1950 }
1951 else if ( internal.indexOf( LOCAL_PACKAGES ) >= 0 )
1952 {
1953 String newInternal = StringUtils.replace( internal, LOCAL_PACKAGES, Processor.printClauses( privatePkgs ) );
1954 properties.setProperty( Analyzer.PRIVATE_PACKAGE, newInternal );
1955 }
1956 }
1957
1958
1959 private static String getPackageName( String filename )
1960 {
1961 int n = filename.lastIndexOf( File.separatorChar );
1962 return n < 0 ? "." : filename.substring( 0, n ).replace( File.separatorChar, '.' );
1963 }
1964
1965
1966 private static List<Resource> getMavenResources( MavenProject currentProject, boolean test )
1967 {
1968 List<Resource> resources = new ArrayList<Resource>( test ? currentProject.getTestResources() : currentProject.getResources() );
1969
1970 if ( currentProject.getCompileSourceRoots() != null )
1971 {
1972
1973 final List<String> packageInfoIncludes = Collections.singletonList( "**/packageinfo" );
1974 for ( Iterator<String> i = currentProject.getCompileSourceRoots().iterator(); i.hasNext(); )
1975 {
1976 String sourceRoot = i.next();
1977 Resource packageInfoResource = new Resource();
1978 packageInfoResource.setDirectory( sourceRoot );
1979 packageInfoResource.setIncludes( packageInfoIncludes );
1980 resources.add( packageInfoResource );
1981 }
1982 }
1983
1984 return resources;
1985 }
1986
1987
1988 protected static String getMavenResourcePaths( MavenProject currentProject, boolean test )
1989 {
1990 final String basePath = currentProject.getBasedir().getAbsolutePath();
1991
1992 Set<String> pathSet = new LinkedHashSet<String>();
1993 for ( Iterator<Resource> i = getMavenResources( currentProject, test ).iterator(); i.hasNext(); )
1994 {
1995 Resource resource = i.next();
1996
1997 final String sourcePath = resource.getDirectory();
1998 final String targetPath = resource.getTargetPath();
1999
2000
2001 if ( new File( sourcePath ).exists() && ( ( targetPath == null ) || ( targetPath.indexOf( ".." ) < 0 ) ) )
2002 {
2003 DirectoryScanner scanner = new DirectoryScanner();
2004
2005 scanner.setBasedir( sourcePath );
2006 if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() )
2007 {
2008 scanner.setIncludes( resource.getIncludes().toArray( EMPTY_STRING_ARRAY ) );
2009 }
2010 else
2011 {
2012 scanner.setIncludes( DEFAULT_INCLUDES );
2013 }
2014
2015 if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() )
2016 {
2017 scanner.setExcludes( resource.getExcludes().toArray( EMPTY_STRING_ARRAY ) );
2018 }
2019
2020 scanner.addDefaultExcludes();
2021 scanner.scan();
2022
2023 List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );
2024
2025 for ( Iterator<String> j = includedFiles.iterator(); j.hasNext(); )
2026 {
2027 String name = j.next();
2028 String path = sourcePath + '/' + name;
2029
2030
2031 if ( path.startsWith( basePath ) )
2032 {
2033 if ( path.length() == basePath.length() )
2034 {
2035 path = ".";
2036 }
2037 else
2038 {
2039 path = path.substring( basePath.length() + 1 );
2040 }
2041 }
2042
2043
2044
2045 if ( File.separatorChar != '/' )
2046 {
2047 name = name.replace( File.separatorChar, '/' );
2048 path = path.replace( File.separatorChar, '/' );
2049 }
2050
2051
2052 path = name + '=' + path;
2053 if ( targetPath != null )
2054 {
2055 path = targetPath + '/' + path;
2056 }
2057
2058
2059 if ( resource.isFiltering() )
2060 {
2061 path = '{' + path + '}';
2062 }
2063
2064 pathSet.add( path );
2065 }
2066 }
2067 }
2068
2069 StringBuffer resourcePaths = new StringBuffer();
2070 for ( Iterator<String> i = pathSet.iterator(); i.hasNext(); )
2071 {
2072 resourcePaths.append( i.next() );
2073 if ( i.hasNext() )
2074 {
2075 resourcePaths.append( ',' );
2076 }
2077 }
2078
2079 return resourcePaths.toString();
2080 }
2081
2082
2083 protected Collection<Artifact> getEmbeddableArtifacts( MavenProject currentProject, DependencyNode dependencyGraph, Analyzer analyzer )
2084 throws MojoExecutionException
2085 {
2086 final Collection<Artifact> artifacts;
2087
2088 String embedTransitive = analyzer.getProperty( DependencyEmbedder.EMBED_TRANSITIVE );
2089 if ( Boolean.valueOf( embedTransitive ).booleanValue() )
2090 {
2091
2092 artifacts = currentProject.getArtifacts();
2093 }
2094 else
2095 {
2096
2097 artifacts = currentProject.getDependencyArtifacts();
2098 }
2099
2100 return getSelectedDependencies( dependencyGraph, artifacts );
2101 }
2102
2103
2104 protected static void addMavenSourcePath( MavenProject currentProject, Analyzer analyzer, Log log )
2105 {
2106
2107 StringBuilder mavenSourcePaths = new StringBuilder();
2108 StringBuilder mavenTestSourcePaths = new StringBuilder();
2109 Map<StringBuilder, List<String>> map = new HashMap<StringBuilder, List<String>>(2);
2110 map.put(mavenSourcePaths, currentProject.getCompileSourceRoots() );
2111 map.put(mavenTestSourcePaths, currentProject.getTestCompileSourceRoots() );
2112 for ( Map.Entry<StringBuilder, List<String>> entry : map.entrySet() )
2113 {
2114 List<String> compileSourceRoots = entry.getValue();
2115 if ( compileSourceRoots != null )
2116 {
2117 StringBuilder sourcePaths = entry.getKey();
2118 for ( Iterator<String> i = compileSourceRoots.iterator(); i.hasNext(); )
2119 {
2120 if ( sourcePaths.length() > 0 )
2121 {
2122 sourcePaths.append( ',' );
2123 }
2124 sourcePaths.append( i.next() );
2125 }
2126 }
2127 }
2128 final String sourcePath = analyzer.getProperty( Analyzer.SOURCEPATH );
2129 if ( sourcePath != null )
2130 {
2131 if ( sourcePath.contains(MAVEN_SOURCES) || sourcePath.contains(MAVEN_TEST_RESOURCES) )
2132 {
2133 String combinedSource = StringUtils.replace( sourcePath, MAVEN_SOURCES, mavenSourcePaths.toString() );
2134 combinedSource = StringUtils.replace( combinedSource, MAVEN_TEST_SOURCES, mavenTestSourcePaths.toString() );
2135 if ( combinedSource.length() > 0 )
2136 {
2137 analyzer.setProperty( Analyzer.SOURCEPATH, combinedSource );
2138 }
2139 else
2140 {
2141 analyzer.unsetProperty( Analyzer.SOURCEPATH );
2142 }
2143 }
2144 else if ( mavenSourcePaths.length() > 0 )
2145 {
2146 log.warn( Analyzer.SOURCEPATH + ": overriding " + mavenSourcePaths + " with " + sourcePath + " (add "
2147 + MAVEN_SOURCES + " if you want to include the maven sources)" );
2148 }
2149 else if ( mavenTestSourcePaths.length() > 0 )
2150 {
2151 log.warn( Analyzer.SOURCEPATH + ": overriding " + mavenTestSourcePaths + " with " + sourcePath + " (add "
2152 + MAVEN_TEST_SOURCES + " if you want to include the maven test sources)" );
2153 }
2154 }
2155 else if ( mavenSourcePaths.length() > 0 )
2156 {
2157 analyzer.setProperty( Analyzer.SOURCEPATH, mavenSourcePaths.toString() );
2158 }
2159 }
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170 protected static void includeJava9Fixups(MavenProject currentProject, Analyzer analyzer)
2171 {
2172 final String classesInWrongDirError = "Classes found in the wrong directory";
2173 final String newFixup = "Classes found in the wrong directory;"
2174 + Analyzer.FIXUPMESSAGES_IS_DIRECTIVE + "="
2175 + Analyzer.FIXUPMESSAGES_IS_WARNING;
2176
2177 String fixups = analyzer.getProperty(Analyzer.FIXUPMESSAGES);
2178 if (fixups != null && !fixups.isEmpty()) {
2179 if (!fixups.contains(classesInWrongDirError)) {
2180 fixups += "," + newFixup;
2181 }
2182 } else {
2183 fixups = newFixup;
2184 }
2185 analyzer.setProperty(Analyzer.FIXUPMESSAGES, fixups);
2186 }
2187
2188 }