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