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 Jar[] jars = new Jar[ classpath.length ];
602 for ( int i = 0; i < classpath.length; i++ ) {
603 jars[i] = new Jar( classpath[i].id, classpath[i].file );
604 }
605 builder.setClasspath( jars );
606 }
607
608 return builder;
609 }
610
611
612 protected static Properties sanitize( Properties properties )
613 {
614
615 Properties sanitizedEntries = new Properties();
616 for ( Iterator<Map.Entry<Object,Object>> itr = properties.entrySet().iterator(); itr.hasNext(); )
617 {
618 Map.Entry<Object,Object> entry = itr.next();
619 if ( !(entry.getKey() instanceof String) )
620 {
621 String key = sanitize(entry.getKey());
622 if ( !properties.containsKey( key ) )
623 {
624 sanitizedEntries.setProperty( key, sanitize( entry.getValue() ) );
625 }
626 itr.remove();
627 }
628 else if ( !(entry.getValue() instanceof String) )
629 {
630 entry.setValue( sanitize( entry.getValue() ) );
631 }
632 }
633 properties.putAll( sanitizedEntries );
634 return properties;
635 }
636
637
638 protected static String sanitize( Object value )
639 {
640 if ( value instanceof String )
641 {
642 return ( String ) value;
643 }
644 else if ( value instanceof Iterable )
645 {
646 String delim = "";
647 StringBuilder buf = new StringBuilder();
648 for ( Object i : ( Iterable<?> ) value )
649 {
650 buf.append( delim ).append( i );
651 delim = ", ";
652 }
653 return buf.toString();
654 }
655 else if ( value.getClass().isArray() )
656 {
657 String delim = "";
658 StringBuilder buf = new StringBuilder();
659 for ( int i = 0, len = Array.getLength( value ); i < len; i++ )
660 {
661 buf.append( delim ).append( Array.get( value, i ) );
662 delim = ", ";
663 }
664 return buf.toString();
665 }
666 else
667 {
668 return String.valueOf( value );
669 }
670 }
671
672
673 protected void addMavenInstructions(MavenProject currentProject, Builder builder) throws Exception
674 {
675 if ( currentProject.getBasedir() != null )
676 {
677
678 includeMavenResources(currentProject, builder, getLog());
679
680
681 includeJava9Fixups(currentProject, builder);
682
683
684 addLocalPackages(outputDirectory, builder);
685
686
687 addMavenSourcePath(currentProject, builder, getLog());
688 }
689
690
691 Collection<Artifact> embeddableArtifacts = getEmbeddableArtifacts( currentProject, builder );
692 DependencyEmbedder dependencyEmbedder = new DependencyEmbedder(getLog(), embeddableArtifacts);
693 dependencyEmbedder.processHeaders(builder);
694
695 Collection<Artifact> embeddedArtifacts = dependencyEmbedder.getEmbeddedArtifacts();
696 if ( !embeddedArtifacts.isEmpty() && createDependencyReducedPom )
697 {
698 Set<String> embeddedIds = new HashSet<String>();
699 for ( Artifact artifact : embeddedArtifacts )
700 {
701 embeddedIds.add( getId( artifact ) );
702 }
703 createDependencyReducedPom( embeddedIds );
704
705 }
706
707 if ( dumpInstructions != null || getLog().isDebugEnabled() )
708 {
709 StringBuilder buf = new StringBuilder();
710 getLog().debug( "BND Instructions:" + NL + dumpInstructions( builder.getProperties(), buf ) );
711 if ( dumpInstructions != null )
712 {
713 getLog().info( "Writing BND instructions to " + dumpInstructions );
714 dumpInstructions.getParentFile().mkdirs();
715 FileUtils.fileWrite( dumpInstructions, "# BND instructions" + NL + buf );
716 }
717 }
718
719
720
721 if ( dumpClasspath != null || getLog().isDebugEnabled() )
722 {
723 StringBuilder buf = new StringBuilder();
724 getLog().debug("BND Classpath:" + NL + dumpClasspath(builder.getClasspath(), buf));
725 if ( dumpClasspath != null )
726 {
727 getLog().info( "Writing BND classpath to " + dumpClasspath );
728 dumpClasspath.getParentFile().mkdirs();
729 FileUtils.fileWrite( dumpClasspath, "# BND classpath" + NL + buf );
730 }
731 }
732 }
733
734
735
736
737 private void createDependencyReducedPom( Set<String> artifactsToRemove )
738 throws IOException, ProjectBuildingException, DependencyTreeBuilderException {
739 Model model = project.getOriginalModel();
740 List<Dependency> dependencies = new ArrayList<Dependency>();
741
742 boolean modified = false;
743
744 List<Dependency> transitiveDeps = new ArrayList<Dependency>();
745
746 for ( Artifact artifact : project.getArtifacts() )
747 {
748 if ( "pom".equals( artifact.getType() ) )
749 {
750
751 continue;
752 }
753
754
755 Dependency dep = new Dependency();
756 dep.setArtifactId( artifact.getArtifactId() );
757 if ( artifact.hasClassifier() )
758 {
759 dep.setClassifier( artifact.getClassifier() );
760 }
761 dep.setGroupId( artifact.getGroupId() );
762 dep.setOptional( artifact.isOptional() );
763 dep.setScope( artifact.getScope() );
764 dep.setType( artifact.getType() );
765 dep.setVersion( artifact.getVersion() );
766
767
768
769 transitiveDeps.add( dep );
770 }
771 List<Dependency> origDeps = project.getDependencies();
772
773 for (Dependency d : origDeps)
774 {
775 dependencies.add(d);
776
777 String id = getId(d);
778
779 if (artifactsToRemove.contains(id))
780 {
781 modified = true;
782
783 dependencies.remove(d);
784 }
785 }
786
787
788 if ( modified )
789 {
790 while ( modified )
791 {
792
793 model.setDependencies( dependencies );
794
795 if ( dependencyReducedPomLocation == null )
796 {
797
798 dependencyReducedPomLocation = new File ( project.getBasedir(), "dependency-reduced-pom.xml" );
799 }
800
801 File f = dependencyReducedPomLocation;
802 if ( f.exists() )
803 {
804 f.delete();
805 }
806
807 Writer w = WriterFactory.newXmlWriter( f );
808
809 String origRelativePath = null;
810 String replaceRelativePath = null;
811 if ( model.getParent() != null)
812 {
813 origRelativePath = model.getParent().getRelativePath();
814
815 }
816 replaceRelativePath = origRelativePath;
817
818 if ( origRelativePath == null )
819 {
820 origRelativePath = "../pom.xml";
821 }
822
823 if ( model.getParent() != null )
824 {
825 File parentFile = new File( project.getBasedir(), model.getParent().getRelativePath() ).getCanonicalFile();
826 if ( !parentFile.isFile() )
827 {
828 parentFile = new File( parentFile, "pom.xml");
829 }
830
831 parentFile = parentFile.getCanonicalFile();
832
833 String relPath = RelativizePath.convertToRelativePath( parentFile, f );
834 model.getParent().setRelativePath( relPath );
835 }
836
837 try
838 {
839 PomWriter.write( w, model, true );
840 }
841 finally
842 {
843 if ( model.getParent() != null )
844 {
845 model.getParent().setRelativePath( replaceRelativePath );
846 }
847 w.close();
848 }
849
850 MavenProject p2 = mavenProjectBuilder.build( f, localRepository, null );
851 modified = updateExcludesInDeps( p2, dependencies, transitiveDeps );
852
853 }
854
855 project.setFile( dependencyReducedPomLocation );
856 }
857 }
858
859 private String getId( Artifact artifact )
860 {
861 return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier() );
862 }
863
864 private String getId( Dependency dependency )
865 {
866 return getId( dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(),
867 dependency.getClassifier() );
868 }
869
870 private String getId( String groupId, String artifactId, String type, String classifier )
871 {
872 return groupId + ":" + artifactId + ":" + type + ":" + ( ( classifier != null ) ? classifier : "" );
873 }
874
875 public boolean updateExcludesInDeps( MavenProject project, List<Dependency> dependencies, List<Dependency> transitiveDeps )
876 throws DependencyTreeBuilderException
877 {
878 org.apache.maven.shared.dependency.tree.DependencyNode node = dependencyTreeBuilder.buildDependencyTree(project, localRepository, artifactFactory,
879 artifactMetadataSource, null,
880 artifactCollector);
881 boolean modified = false;
882 for (org.apache.maven.shared.dependency.tree.DependencyNode n2 : node.getChildren())
883 {
884 for (org.apache.maven.shared.dependency.tree.DependencyNode n3 : n2.getChildren())
885 {
886
887
888
889 if (n3.getState() == org.apache.maven.shared.dependency.tree.DependencyNode.INCLUDED)
890 {
891
892
893
894
895
896
897 boolean found = false;
898 for (Dependency dep : transitiveDeps)
899 {
900 if (dep.getArtifactId().equals(n3.getArtifact().getArtifactId()) && dep.getGroupId().equals(
901 n3.getArtifact().getGroupId()))
902 {
903 found = true;
904 }
905
906 }
907
908 if (!found)
909 {
910 for (Dependency dep : dependencies)
911 {
912 if (dep.getArtifactId().equals(n2.getArtifact().getArtifactId())
913 && dep.getGroupId().equals(n2.getArtifact().getGroupId()))
914 {
915 Exclusion exclusion = new Exclusion();
916 exclusion.setArtifactId(n3.getArtifact().getArtifactId());
917 exclusion.setGroupId(n3.getArtifact().getGroupId());
918 dep.addExclusion(exclusion);
919 modified = true;
920 break;
921 }
922 }
923 }
924 }
925 }
926 }
927 return modified;
928 }
929
930
931 protected Builder buildOSGiBundle(MavenProject currentProject, Map<String, String> originalInstructions,
932 ClassPathItem[] classpath) throws Exception
933 {
934 Builder builder = getOSGiBuilder( currentProject, originalInstructions, classpath );
935
936 addMavenInstructions( currentProject, builder );
937
938 builder.build();
939
940 mergeMavenManifest(currentProject, builder);
941
942 return builder;
943 }
944
945
946 protected static StringBuilder dumpInstructions( Properties properties, StringBuilder buf )
947 {
948 try
949 {
950 buf.append( "#-----------------------------------------------------------------------" + NL );
951 Properties stringProperties = new Properties();
952 for ( Enumeration<String> e = (Enumeration<String>) properties.propertyNames(); e.hasMoreElements(); )
953 {
954
955 String key = e.nextElement();
956 String value = properties.getProperty( key );
957 if ( value != null )
958 {
959 stringProperties.setProperty( key, value );
960 }
961 }
962 ByteArrayOutputStream out = new ByteArrayOutputStream();
963 stringProperties.store( out, null );
964 buf.append( out.toString( "8859_1" ) );
965 buf.append("#-----------------------------------------------------------------------").append(NL);
966 }
967 catch ( Throwable e )
968 {
969
970 }
971 return buf;
972 }
973
974
975 protected static StringBuilder dumpClasspath( List<Jar> classpath, StringBuilder buf )
976 {
977 try
978 {
979 buf.append("#-----------------------------------------------------------------------").append(NL);
980 buf.append("-classpath:\\").append(NL);
981 for ( Iterator<Jar> i = classpath.iterator(); i.hasNext(); )
982 {
983 File path = i.next().getSource();
984 if ( path != null )
985 {
986 buf.append(' ').append(path.toString()).append(i.hasNext() ? ",\\" : "").append(NL);
987 }
988 }
989 buf.append("#-----------------------------------------------------------------------").append(NL);
990 }
991 catch ( Throwable e )
992 {
993
994 }
995 return buf;
996 }
997
998
999 protected static StringBuilder dumpManifest( Manifest manifest, StringBuilder buf )
1000 {
1001 try
1002 {
1003 buf.append("#-----------------------------------------------------------------------").append(NL);
1004 ByteArrayOutputStream out = new ByteArrayOutputStream();
1005 ManifestWriter.outputManifest(manifest, out, true);
1006 buf.append( out.toString( "UTF8" ) );
1007 buf.append("#-----------------------------------------------------------------------").append(NL);
1008 }
1009 catch ( Throwable e )
1010 {
1011
1012 }
1013 return buf;
1014 }
1015
1016
1017 protected static void includeMavenResources( MavenProject currentProject, Analyzer analyzer, Log log )
1018 {
1019
1020 final String mavenResourcePaths = getMavenResourcePaths( currentProject, false );
1021 final String mavenTestResourcePaths = getMavenResourcePaths( currentProject, true );
1022 final String includeResource = analyzer.getProperty( Analyzer.INCLUDE_RESOURCE );
1023 if ( includeResource != null )
1024 {
1025 if ( includeResource.contains( MAVEN_RESOURCES ) || includeResource.contains( MAVEN_TEST_RESOURCES ) )
1026 {
1027 String combinedResource = StringUtils.replace( includeResource, MAVEN_RESOURCES, mavenResourcePaths );
1028 combinedResource = StringUtils.replace( combinedResource, MAVEN_TEST_RESOURCES, mavenTestResourcePaths );
1029 if ( combinedResource.length() > 0 )
1030 {
1031 analyzer.setProperty( Analyzer.INCLUDE_RESOURCE, combinedResource );
1032 }
1033 else
1034 {
1035 analyzer.unsetProperty( Analyzer.INCLUDE_RESOURCE );
1036 }
1037 }
1038 else if ( mavenResourcePaths.length() > 0 )
1039 {
1040 log.warn( Analyzer.INCLUDE_RESOURCE + ": overriding " + mavenResourcePaths + " with " + includeResource
1041 + " (add " + MAVEN_RESOURCES + " if you want to include the maven resources)" );
1042 }
1043 }
1044 else if ( mavenResourcePaths.length() > 0 )
1045 {
1046 analyzer.setProperty( Analyzer.INCLUDE_RESOURCE, mavenResourcePaths );
1047 }
1048 }
1049
1050
1051 protected void mergeMavenManifest(MavenProject currentProject, Builder builder) throws Exception
1052 {
1053 Jar jar = builder.getJar();
1054
1055 if ( getLog().isDebugEnabled() )
1056 {
1057 getLog().debug( "BND Manifest:" + NL + dumpManifest( jar.getManifest(), new StringBuilder() ) );
1058 }
1059
1060 boolean addMavenDescriptor = currentProject.getBasedir() != null;
1061
1062 try
1063 {
1064
1065
1066
1067 MavenArchiveConfiguration archiveConfig = JarPluginConfiguration.getArchiveConfiguration( currentProject );
1068 String mavenManifestText = new MavenArchiver().getManifest( currentProject, archiveConfig ).toString();
1069 addMavenDescriptor = addMavenDescriptor && archiveConfig.isAddMavenDescriptor();
1070
1071 Manifest mavenManifest = new Manifest();
1072
1073
1074 File externalManifestFile = archiveConfig.getManifestFile();
1075 if ( null != externalManifestFile )
1076 {
1077 if ( !externalManifestFile.isAbsolute() )
1078 {
1079 externalManifestFile = new File( currentProject.getBasedir(), externalManifestFile.getPath() );
1080 }
1081 if ( externalManifestFile.exists() && !externalManifestFile.equals( new File( manifestLocation, "MANIFEST.MF" ) ) )
1082 {
1083 InputStream mis = new FileInputStream( externalManifestFile );
1084 mavenManifest.read( mis );
1085 mis.close();
1086 }
1087 }
1088
1089
1090 mavenManifest.read( new ByteArrayInputStream( mavenManifestText.getBytes( "UTF8" ) ) );
1091
1092 if ( !archiveConfig.isManifestSectionsEmpty() )
1093 {
1094
1095
1096
1097 List<ManifestSection> sections = archiveConfig.getManifestSections();
1098 for ( Iterator<ManifestSection> i = sections.iterator(); i.hasNext(); )
1099 {
1100 ManifestSection section = i.next();
1101 Attributes attributes = new Attributes();
1102
1103 if ( !section.isManifestEntriesEmpty() )
1104 {
1105 Map<String, String> entries = section.getManifestEntries();
1106 for ( Iterator<Map.Entry<String, String>> j = entries.entrySet().iterator(); j.hasNext(); )
1107 {
1108 Map.Entry<String, String> entry = j.next();
1109 attributes.putValue( entry.getKey(), entry.getValue() );
1110 }
1111 }
1112
1113 mavenManifest.getEntries().put( section.getName(), attributes );
1114 }
1115 }
1116
1117 Attributes mainMavenAttributes = mavenManifest.getMainAttributes();
1118 mainMavenAttributes.putValue( "Created-By", "Apache Maven Bundle Plugin" );
1119
1120 String[] removeHeaders = builder.getProperty( Constants.REMOVEHEADERS, "" ).split( "," );
1121
1122
1123 for ( int i = 0; i < removeHeaders.length; i++ )
1124 {
1125 for ( Iterator<Object> j = mainMavenAttributes.keySet().iterator(); j.hasNext(); )
1126 {
1127 if ( j.next().toString().matches( removeHeaders[i].trim() ) )
1128 {
1129 j.remove();
1130 }
1131 }
1132 }
1133
1134
1135
1136
1137 Properties properties = builder.getProperties();
1138 Manifest bundleManifest = jar.getManifest();
1139 if ( properties.containsKey( "Merge-Headers" ) )
1140 {
1141 Instructions instructions = new Instructions( ExtList.from(builder.getProperty("Merge-Headers")) );
1142 mergeManifest( instructions, bundleManifest, mavenManifest );
1143 }
1144 else
1145 {
1146 bundleManifest.getMainAttributes().putAll( mainMavenAttributes );
1147 bundleManifest.getEntries().putAll( mavenManifest.getEntries() );
1148 }
1149
1150
1151
1152 String importPackages = bundleManifest.getMainAttributes().getValue( "Import-Package" );
1153 if ( importPackages != null )
1154 {
1155 Set optionalPackages = getOptionalPackages( currentProject);
1156
1157 Map<String, ? extends Map<String, String>> values;
1158 try (Analyzer analyzer = new Analyzer()) {
1159 values = analyzer.parseHeader( importPackages );
1160 }
1161 for ( Map.Entry<String, ? extends Map<String, String>> entry : values.entrySet() )
1162 {
1163 String pkg = entry.getKey();
1164 Map<String, String> options = entry.getValue();
1165 if ( !options.containsKey( "resolution:" ) && optionalPackages.contains( pkg ) )
1166 {
1167 options.put( "resolution:", "optional" );
1168 }
1169 }
1170 String result = Processor.printClauses( values );
1171 bundleManifest.getMainAttributes().putValue( "Import-Package", result );
1172 }
1173
1174 jar.setManifest( bundleManifest );
1175 }
1176 catch ( Exception e )
1177 {
1178 getLog().warn( "Unable to merge Maven manifest: " + e.getLocalizedMessage() );
1179 }
1180
1181 if ( addMavenDescriptor )
1182 {
1183 doMavenMetadata( currentProject, jar );
1184 }
1185
1186 if ( getLog().isDebugEnabled() )
1187 {
1188 getLog().debug( "Final Manifest:" + NL + dumpManifest( jar.getManifest(), new StringBuilder() ) );
1189 }
1190
1191 builder.setJar( jar );
1192 }
1193
1194
1195 protected static void mergeManifest( Instructions instructions, Manifest... manifests ) throws IOException
1196 {
1197 for ( int i = manifests.length - 2; i >= 0; i-- )
1198 {
1199 Manifest mergedManifest = manifests[i];
1200 Manifest manifest = manifests[i + 1];
1201 Attributes mergedMainAttributes = mergedManifest.getMainAttributes();
1202 Attributes mainAttributes = manifest.getMainAttributes();
1203 Attributes filteredMainAttributes = filterAttributes( instructions, mainAttributes, null );
1204 if ( !filteredMainAttributes.isEmpty() )
1205 {
1206 mergeAttributes( mergedMainAttributes, filteredMainAttributes );
1207 }
1208 Map<String, Attributes> mergedEntries = mergedManifest.getEntries();
1209 Map<String, Attributes> entries = manifest.getEntries();
1210 for ( Map.Entry<String, Attributes> entry : entries.entrySet() )
1211 {
1212 String name = entry.getKey();
1213 Attributes attributes = entry.getValue();
1214 Attributes filteredAttributes = filterAttributes( instructions, attributes, null );
1215 if ( !filteredAttributes.isEmpty() )
1216 {
1217 Attributes mergedAttributes = mergedManifest.getAttributes( name );
1218 if ( mergedAttributes != null)
1219 {
1220 mergeAttributes(mergedAttributes, filteredAttributes);
1221 }
1222 else
1223 {
1224 mergedEntries.put(name, filteredAttributes);
1225 }
1226 }
1227 }
1228 }
1229 }
1230
1231
1232
1233
1234
1235 private static Attributes filterAttributes(Instructions instructions, Attributes source, Set<Instruction> nomatch) {
1236 Attributes result = new Attributes();
1237 Map<String, Object> keys = new TreeMap<String, Object>();
1238 for ( Object key : source.keySet() )
1239 {
1240 keys.put( key.toString(), key );
1241 }
1242
1243 List<Instruction> filters = new ArrayList<Instruction>( instructions.keySet() );
1244 if (nomatch == null)
1245 {
1246 nomatch = Create.set();
1247 }
1248 for ( Instruction instruction : filters ) {
1249 boolean match = false;
1250 for (Iterator<Map.Entry<String, Object>> i = keys.entrySet().iterator(); i.hasNext();)
1251 {
1252 Map.Entry<String, Object> entry = i.next();
1253 String key = entry.getKey();
1254 if ( instruction.matches( key ) )
1255 {
1256 match = true;
1257 if (!instruction.isNegated()) {
1258 Object name = entry.getValue();
1259 Object value = source.get( name );
1260 result.put( name, value );
1261 }
1262 i.remove();
1263 }
1264 }
1265 if (!match && !instruction.isAny())
1266 nomatch.add(instruction);
1267 }
1268
1269
1270
1271
1272
1273
1274
1275
1276 for (Iterator<Instruction> i = nomatch.iterator(); i.hasNext();) {
1277 Instruction instruction = i.next();
1278
1279
1280
1281
1282
1283 if (instruction.isLiteral() && !instruction.isNegated()) {
1284 Object key = keys.get( instruction.getLiteral() );
1285 if ( key != null )
1286 {
1287 Object value = source.get( key );
1288 result.put( key, value );
1289 }
1290 i.remove();
1291 continue;
1292 }
1293
1294
1295
1296
1297
1298 if (instruction.isNegated()) {
1299 i.remove();
1300 continue;
1301 }
1302
1303
1304
1305 if (instruction.isOptional()) {
1306 i.remove();
1307 continue;
1308 }
1309 }
1310 return result;
1311 }
1312
1313
1314 private static void mergeAttributes( Attributes... attributesArray ) throws IOException
1315 {
1316 for ( int i = attributesArray.length - 2; i >= 0; i-- )
1317 {
1318 Attributes mergedAttributes = attributesArray[i];
1319 Attributes attributes = attributesArray[i + 1];
1320 for ( Map.Entry<Object, Object> entry : attributes.entrySet() )
1321 {
1322 Object name = entry.getKey();
1323 String value = (String) entry.getValue();
1324 String oldValue = (String) mergedAttributes.put( name, value );
1325 if ( oldValue != null )
1326 {
1327 Parameters mergedClauses = OSGiHeader.parseHeader(oldValue);
1328 Parameters clauses = OSGiHeader.parseHeader( value );
1329 if ( !mergedClauses.isEqual( clauses) )
1330 {
1331 for ( Map.Entry<String, Attrs> clauseEntry : clauses.entrySet() )
1332 {
1333 String clause = clauseEntry.getKey();
1334 Attrs attrs = clauseEntry.getValue();
1335 Attrs mergedAttrs = mergedClauses.get( clause );
1336 if ( mergedAttrs == null)
1337 {
1338 mergedClauses.put( clause, attrs );
1339 }
1340 else if ( !mergedAttrs.isEqual(attrs) )
1341 {
1342 for ( Map.Entry<String,String> adentry : attrs.entrySet() )
1343 {
1344 String adname = adentry.getKey();
1345 String ad = adentry.getValue();
1346 if ( mergedAttrs.containsKey( adname ) )
1347 {
1348 Attrs.Type type = attrs.getType( adname );
1349 switch (type)
1350 {
1351 case VERSIONS:
1352 case STRINGS:
1353 case LONGS:
1354 case DOUBLES:
1355 ExtList<String> mergedAd = ExtList.from( mergedAttrs.get( adname ) );
1356 ExtList.from( ad ).addAll( ExtList.from( ad ) );
1357 mergedAttrs.put(adname, mergedAd.join() );
1358 break;
1359 }
1360 }
1361 else
1362 {
1363 mergedAttrs.put( adname, ad );
1364 }
1365 }
1366 }
1367 }
1368 mergedAttributes.put( name, Processor.printClauses( mergedClauses ) );
1369 }
1370 }
1371 }
1372 }
1373 }
1374
1375
1376 protected Set<String> getOptionalPackages(MavenProject currentProject) throws IOException, MojoExecutionException
1377 {
1378 ArrayList<Artifact> inscope = new ArrayList<Artifact>();
1379 final Collection<Artifact> artifacts = getSelectedDependencies(currentProject.getArtifacts() );
1380 for ( Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); )
1381 {
1382 Artifact artifact = it.next();
1383 if ( artifact.getArtifactHandler().isAddedToClasspath() )
1384 {
1385 inscope.add( artifact );
1386 }
1387 }
1388
1389 HashSet<String> optionalArtifactIds = new HashSet<String>();
1390 for ( Iterator<Artifact> it = inscope.iterator(); it.hasNext(); )
1391 {
1392 Artifact artifact = it.next();
1393 if ( artifact.isOptional() )
1394 {
1395 String id = artifact.toString();
1396 if ( artifact.getScope() != null )
1397 {
1398
1399 id = id.replaceFirst( ":[^:]*$", "" );
1400 }
1401 optionalArtifactIds.add( id );
1402 }
1403
1404 }
1405
1406 HashSet<String> required = new HashSet<String>();
1407 HashSet<String> optional = new HashSet<String>();
1408 for ( Iterator<Artifact> it = inscope.iterator(); it.hasNext(); )
1409 {
1410 Artifact artifact = it.next();
1411 File file = getFile( artifact );
1412 if ( file == null )
1413 {
1414 continue;
1415 }
1416
1417 Jar jar = new Jar( artifact.getArtifactId(), file );
1418 if ( isTransitivelyOptional( optionalArtifactIds, artifact ) )
1419 {
1420 optional.addAll( jar.getPackages() );
1421 }
1422 else
1423 {
1424 required.addAll( jar.getPackages() );
1425 }
1426 jar.close();
1427 }
1428
1429 optional.removeAll( required );
1430 return optional;
1431 }
1432
1433
1434
1435
1436
1437
1438
1439
1440 protected boolean isTransitivelyOptional( HashSet<String> optionalArtifactIds, Artifact artifact )
1441 {
1442 List<String> trail = artifact.getDependencyTrail();
1443 for ( Iterator<String> iterator = trail.iterator(); iterator.hasNext(); )
1444 {
1445 String next = iterator.next();
1446 if ( optionalArtifactIds.contains( next ) )
1447 {
1448 return true;
1449 }
1450 }
1451 return false;
1452 }
1453
1454
1455 private void unpackBundle( File jarFile )
1456 {
1457 File outputDir = getOutputDirectory();
1458 if ( null == outputDir )
1459 {
1460 outputDir = new File( getBuildDirectory(), "classes" );
1461 }
1462
1463 try
1464 {
1465
1466
1467
1468
1469 if ( !outputDir.exists() )
1470 {
1471 outputDir.mkdirs();
1472 }
1473
1474 UnArchiver unArchiver = m_archiverManager.getUnArchiver( "jar" );
1475 unArchiver.setDestDirectory( outputDir );
1476 unArchiver.setSourceFile( jarFile );
1477 unArchiver.extract();
1478 }
1479 catch ( Exception e )
1480 {
1481 getLog().error( "Problem unpacking " + jarFile + " to " + outputDir, e );
1482 }
1483 }
1484
1485
1486 protected static String removeTagFromInstruction( String instruction, String tag )
1487 {
1488 StringBuffer buf = new StringBuffer();
1489
1490 String[] clauses = instruction.split( "," );
1491 for ( int i = 0; i < clauses.length; i++ )
1492 {
1493 String clause = clauses[i].trim();
1494 if ( !tag.equals( clause ) )
1495 {
1496 if ( buf.length() > 0 )
1497 {
1498 buf.append( ',' );
1499 }
1500 buf.append( clause );
1501 }
1502 }
1503
1504 return buf.toString();
1505 }
1506
1507
1508 private static Map<String, String> getProperties( Model projectModel, String prefix )
1509 {
1510 Map<String, String> properties = new LinkedHashMap<String, String>();
1511 Method methods[] = Model.class.getDeclaredMethods();
1512 for ( int i = 0; i < methods.length; i++ )
1513 {
1514 String name = methods[i].getName();
1515 if ( name.startsWith( "get" ) )
1516 {
1517 try
1518 {
1519 Object v = methods[i].invoke( projectModel, null );
1520 if ( v != null )
1521 {
1522 name = prefix + Character.toLowerCase( name.charAt( 3 ) ) + name.substring( 4 );
1523 if ( v.getClass().isArray() )
1524 properties.put( name, Arrays.asList( ( Object[] ) v ).toString() );
1525 else
1526 properties.put( name, v.toString() );
1527
1528 }
1529 }
1530 catch ( Exception e )
1531 {
1532
1533 }
1534 }
1535 }
1536 return properties;
1537 }
1538
1539
1540 private static StringBuffer printLicenses( List<License> licenses )
1541 {
1542 if ( licenses == null || licenses.size() == 0 )
1543 return null;
1544 StringBuffer sb = new StringBuffer();
1545 String del = "";
1546 for ( Iterator<License> i = licenses.iterator(); i.hasNext(); )
1547 {
1548 License l = i.next();
1549 String url = l.getUrl();
1550 if ( url == null )
1551 continue;
1552 sb.append( del );
1553 sb.append( url );
1554 del = ", ";
1555 }
1556 if ( sb.length() == 0 )
1557 return null;
1558 return sb;
1559 }
1560
1561
1562
1563
1564
1565
1566 private void doMavenMetadata( MavenProject currentProject, Jar jar ) throws IOException
1567 {
1568 String path = "META-INF/maven/" + currentProject.getGroupId() + "/" + currentProject.getArtifactId();
1569
1570 File pomFile = currentProject.getFile();
1571 if ( pomFile == null || !pomFile.exists() )
1572 {
1573 pomFile = new File( currentProject.getBasedir(), "pom.xml" );
1574 }
1575 if ( pomFile.exists() )
1576 {
1577 jar.putResource( path + "/pom.xml", new FileResource( pomFile ) );
1578 }
1579
1580 Properties p = new Properties();
1581 p.put( "version", currentProject.getVersion() );
1582 p.put( "groupId", currentProject.getGroupId() );
1583 p.put( "artifactId", currentProject.getArtifactId() );
1584 ByteArrayOutputStream out = new ByteArrayOutputStream();
1585 p.store( out, "Generated by org.apache.felix.bundleplugin" );
1586 jar.putResource( path + "/pom.properties", new EmbeddedResource( out.toByteArray(), System.currentTimeMillis() ) );
1587 }
1588
1589
1590 protected ClassPathItem[] getClasspath(MavenProject currentProject) throws IOException, MojoExecutionException
1591 {
1592 List<ClassPathItem> list = new ArrayList<ClassPathItem>( currentProject.getArtifacts().size() + 1 );
1593
1594 String d = currentProject.getBuild() != null ? currentProject.getBuild().getOutputDirectory() : null;
1595 if ( d != null )
1596 {
1597 list.add( new ClassPathItem( ".", new File( d ) ) );
1598 }
1599
1600 final Collection<Artifact> artifacts = getSelectedDependencies(currentProject.getArtifacts() );
1601 for ( Artifact artifact : artifacts )
1602 {
1603 if ( artifact.getArtifactHandler().isAddedToClasspath() && !Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
1604 {
1605 File file = getFile( artifact );
1606 if ( file == null )
1607 {
1608 getLog().warn(
1609 "File is not available for artifact " + artifact + " in project "
1610 + currentProject.getArtifact() );
1611 continue;
1612 }
1613 ClassPathItem jar = new ClassPathItem( artifact.getArtifactId(), file );
1614 list.add( jar );
1615 }
1616 }
1617 ClassPathItem[] cp = new ClassPathItem[list.size()];
1618 list.toArray( cp );
1619
1620 return cp;
1621 }
1622
1623
1624 private Collection<Artifact> getSelectedDependencies(Collection<Artifact> artifacts) throws MojoExecutionException
1625 {
1626 if ( null == excludeDependencies || excludeDependencies.length() == 0 )
1627 {
1628 return artifacts;
1629 }
1630 else if ( "true".equalsIgnoreCase( excludeDependencies ) )
1631 {
1632 return Collections.emptyList();
1633 }
1634
1635 Collection<Artifact> selectedDependencies = new LinkedHashSet<Artifact>( artifacts );
1636 DependencyExcluder excluder = new DependencyExcluder(artifacts );
1637 excluder.processHeaders( excludeDependencies );
1638 selectedDependencies.removeAll( excluder.getExcludedArtifacts() );
1639
1640 return selectedDependencies;
1641 }
1642
1643
1644
1645
1646
1647
1648
1649 protected File getFile( Artifact artifact )
1650 {
1651 return artifact.getFile();
1652 }
1653
1654
1655 private static void header( Properties properties, String key, Object value )
1656 {
1657 if ( value == null )
1658 return;
1659
1660 if ( value instanceof Collection && ( ( Collection ) value ).isEmpty() )
1661 return;
1662
1663 properties.put( key, value.toString().replaceAll( "[\r\n]", "" ) );
1664 }
1665
1666
1667
1668
1669
1670
1671
1672
1673 protected String convertVersionToOsgi( String version )
1674 {
1675 return getMaven2OsgiConverter().getVersion( version );
1676 }
1677
1678
1679
1680
1681
1682 protected String getBundleName( MavenProject currentProject )
1683 {
1684 String extension;
1685 try
1686 {
1687 extension = currentProject.getArtifact().getArtifactHandler().getExtension();
1688 }
1689 catch ( Throwable e )
1690 {
1691 extension = currentProject.getArtifact().getType();
1692 }
1693 if ( StringUtils.isEmpty( extension ) || "bundle".equals( extension ) || "pom".equals( extension ) )
1694 {
1695 extension = "jar";
1696 }
1697 if ( null != classifier && classifier.trim().length() > 0 )
1698 {
1699 return finalName + '-' + classifier + '.' + extension;
1700 }
1701 return finalName + '.' + extension;
1702 }
1703
1704
1705 protected String getBuildDirectory()
1706 {
1707 return buildDirectory;
1708 }
1709
1710
1711 protected void setBuildDirectory( String _buildirectory )
1712 {
1713 buildDirectory = _buildirectory;
1714 }
1715
1716
1717 protected Properties getDefaultProperties( MavenProject currentProject )
1718 {
1719 Properties properties = new Properties();
1720
1721 String bsn;
1722 try
1723 {
1724 bsn = getMaven2OsgiConverter().getBundleSymbolicName( currentProject.getArtifact() );
1725 }
1726 catch ( Exception e )
1727 {
1728 bsn = currentProject.getGroupId() + "." + currentProject.getArtifactId();
1729 }
1730
1731
1732 properties.put( MAVEN_SYMBOLICNAME, bsn );
1733 properties.put( Analyzer.BUNDLE_SYMBOLICNAME, bsn );
1734 properties.put( Analyzer.IMPORT_PACKAGE, "*" );
1735 properties.put( Analyzer.BUNDLE_VERSION, getMaven2OsgiConverter().getVersion( currentProject.getVersion() ) );
1736
1737
1738 properties.put( Constants.REMOVEHEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE );
1739
1740 header( properties, Analyzer.BUNDLE_DESCRIPTION, currentProject.getDescription() );
1741 StringBuffer licenseText = printLicenses( currentProject.getLicenses() );
1742 if ( licenseText != null )
1743 {
1744 header( properties, Analyzer.BUNDLE_LICENSE, licenseText );
1745 }
1746 header( properties, Analyzer.BUNDLE_NAME, currentProject.getName() );
1747
1748 if ( currentProject.getOrganization() != null )
1749 {
1750 if ( currentProject.getOrganization().getName() != null )
1751 {
1752 String organizationName = currentProject.getOrganization().getName();
1753 header( properties, Analyzer.BUNDLE_VENDOR, organizationName );
1754 properties.put( "project.organization.name", organizationName );
1755 properties.put( "pom.organization.name", organizationName );
1756 }
1757 if ( currentProject.getOrganization().getUrl() != null )
1758 {
1759 String organizationUrl = currentProject.getOrganization().getUrl();
1760 header( properties, Analyzer.BUNDLE_DOCURL, organizationUrl );
1761 properties.put( "project.organization.url", organizationUrl );
1762 properties.put( "pom.organization.url", organizationUrl );
1763 }
1764 }
1765
1766 properties.putAll( currentProject.getProperties() );
1767 properties.putAll( currentProject.getModel().getProperties() );
1768
1769 for ( Iterator<String> i = currentProject.getFilters().iterator(); i.hasNext(); )
1770 {
1771 File filterFile = new File( i.next() );
1772 if ( filterFile.isFile() )
1773 {
1774 properties.putAll( PropertyUtils.loadProperties( filterFile ) );
1775 }
1776 }
1777
1778 if ( m_mavenSession != null )
1779 {
1780 try
1781 {
1782
1783 Properties sessionProperties = m_mavenSession.getExecutionProperties();
1784 for ( Enumeration<String> e = (Enumeration<String>) sessionProperties.propertyNames(); e.hasMoreElements(); )
1785 {
1786 String key = e.nextElement();
1787 if ( key.length() > 0 && !Character.isUpperCase( key.charAt( 0 ) ) )
1788 {
1789 properties.put( key, sessionProperties.getProperty( key ) );
1790 }
1791 }
1792 }
1793 catch ( Exception e )
1794 {
1795 getLog().warn( "Problem with Maven session properties: " + e.getLocalizedMessage() );
1796 }
1797 }
1798
1799 properties.putAll( getProperties( currentProject.getModel(), "project.build." ) );
1800 properties.putAll( getProperties( currentProject.getModel(), "pom." ) );
1801 properties.putAll( getProperties( currentProject.getModel(), "project." ) );
1802
1803 properties.put( "project.baseDir", getBase( currentProject ) );
1804 properties.put( "project.build.directory", getBuildDirectory() );
1805 properties.put( "project.build.outputdirectory", getOutputDirectory() );
1806
1807 properties.put( "classifier", classifier == null ? "" : classifier );
1808
1809
1810 header( properties, Analyzer.PLUGIN, BlueprintPlugin.class.getName() + ","
1811 + SpringXMLType.class.getName() + ","
1812 + JpaPlugin.class.getName() );
1813
1814 return properties;
1815 }
1816
1817
1818 protected static File getBase( MavenProject currentProject )
1819 {
1820 return currentProject.getBasedir() != null ? currentProject.getBasedir() : new File( "" );
1821 }
1822
1823
1824 protected File getOutputDirectory()
1825 {
1826 return outputDirectory;
1827 }
1828
1829
1830 protected void setOutputDirectory( File _outputDirectory )
1831 {
1832 outputDirectory = _outputDirectory;
1833 }
1834
1835
1836 private static void addLocalPackages( File outputDirectory, Analyzer analyzer ) throws IOException
1837 {
1838 Packages packages = new Packages();
1839
1840 if ( outputDirectory != null && outputDirectory.isDirectory() )
1841 {
1842
1843 DirectoryScanner scanner = new DirectoryScanner();
1844 scanner.setBasedir( outputDirectory );
1845 scanner.setIncludes( new String[]
1846 { "**/*.class" } );
1847
1848 scanner.addDefaultExcludes();
1849 scanner.scan();
1850
1851 String[] paths = scanner.getIncludedFiles();
1852 for ( int i = 0; i < paths.length; i++ )
1853 {
1854 packages.put( analyzer.getPackageRef( getPackageName( paths[i] ) ) );
1855 }
1856 }
1857
1858 Packages exportedPkgs = new Packages();
1859 Packages privatePkgs = new Packages();
1860
1861 boolean noprivatePackages = "!*".equals( analyzer.getProperty( Analyzer.PRIVATE_PACKAGE ) );
1862
1863 for ( PackageRef pkg : packages.keySet() )
1864 {
1865
1866 privatePkgs.put( pkg );
1867
1868
1869 String fqn = pkg.getFQN();
1870 if ( noprivatePackages || !( ".".equals( fqn ) || fqn.contains( ".internal" ) || fqn.contains( ".impl" ) ) )
1871 {
1872 exportedPkgs.put( pkg );
1873 }
1874 }
1875
1876 Properties properties = analyzer.getProperties();
1877 String exported = properties.getProperty( Analyzer.EXPORT_PACKAGE );
1878 if ( exported == null )
1879 {
1880 if ( !properties.containsKey( Analyzer.EXPORT_CONTENTS ) )
1881 {
1882
1883 for ( Attrs attrs : exportedPkgs.values() )
1884 {
1885 attrs.put( Constants.SPLIT_PACKAGE_DIRECTIVE, "merge-first" );
1886 }
1887 properties.setProperty( Analyzer.EXPORT_PACKAGE, Processor.printClauses( exportedPkgs ) );
1888 }
1889 else
1890 {
1891
1892 properties.setProperty( Analyzer.EXPORT_PACKAGE, "" );
1893 }
1894 }
1895 else if ( exported.indexOf( LOCAL_PACKAGES ) >= 0 )
1896 {
1897 String newExported = StringUtils.replace( exported, LOCAL_PACKAGES, Processor.printClauses( exportedPkgs ) );
1898 properties.setProperty( Analyzer.EXPORT_PACKAGE, newExported );
1899 }
1900
1901 String internal = properties.getProperty( Analyzer.PRIVATE_PACKAGE );
1902 if ( internal == null )
1903 {
1904 if ( !privatePkgs.isEmpty() )
1905 {
1906 for ( Attrs attrs : privatePkgs.values() )
1907 {
1908 attrs.put( Constants.SPLIT_PACKAGE_DIRECTIVE, "merge-first" );
1909 }
1910 properties.setProperty( Analyzer.PRIVATE_PACKAGE, Processor.printClauses( privatePkgs ) );
1911 }
1912 else
1913 {
1914
1915 properties.setProperty( Analyzer.PRIVATE_PACKAGE, "!*" );
1916 }
1917 }
1918 else if ( internal.indexOf( LOCAL_PACKAGES ) >= 0 )
1919 {
1920 String newInternal = StringUtils.replace( internal, LOCAL_PACKAGES, Processor.printClauses( privatePkgs ) );
1921 properties.setProperty( Analyzer.PRIVATE_PACKAGE, newInternal );
1922 }
1923 }
1924
1925
1926 private static String getPackageName( String filename )
1927 {
1928 int n = filename.lastIndexOf( File.separatorChar );
1929 return n < 0 ? "." : filename.substring( 0, n ).replace( File.separatorChar, '.' );
1930 }
1931
1932
1933 private static List<Resource> getMavenResources( MavenProject currentProject, boolean test )
1934 {
1935 List<Resource> resources = new ArrayList<Resource>( test ? currentProject.getTestResources() : currentProject.getResources() );
1936
1937 if ( currentProject.getCompileSourceRoots() != null )
1938 {
1939
1940 final List<String> packageInfoIncludes = Collections.singletonList( "**/packageinfo" );
1941 for ( Iterator<String> i = currentProject.getCompileSourceRoots().iterator(); i.hasNext(); )
1942 {
1943 String sourceRoot = i.next();
1944 Resource packageInfoResource = new Resource();
1945 packageInfoResource.setDirectory( sourceRoot );
1946 packageInfoResource.setIncludes( packageInfoIncludes );
1947 resources.add( packageInfoResource );
1948 }
1949 }
1950
1951 return resources;
1952 }
1953
1954
1955 protected static String getMavenResourcePaths( MavenProject currentProject, boolean test )
1956 {
1957 final String basePath = currentProject.getBasedir().getAbsolutePath();
1958
1959 Set<String> pathSet = new LinkedHashSet<String>();
1960 for ( Iterator<Resource> i = getMavenResources( currentProject, test ).iterator(); i.hasNext(); )
1961 {
1962 Resource resource = i.next();
1963
1964 final String sourcePath = resource.getDirectory();
1965 final String targetPath = resource.getTargetPath();
1966
1967
1968 if ( new File( sourcePath ).exists() && ( ( targetPath == null ) || ( targetPath.indexOf( ".." ) < 0 ) ) )
1969 {
1970 DirectoryScanner scanner = new DirectoryScanner();
1971
1972 scanner.setBasedir( sourcePath );
1973 if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() )
1974 {
1975 scanner.setIncludes( resource.getIncludes().toArray( EMPTY_STRING_ARRAY ) );
1976 }
1977 else
1978 {
1979 scanner.setIncludes( DEFAULT_INCLUDES );
1980 }
1981
1982 if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() )
1983 {
1984 scanner.setExcludes( resource.getExcludes().toArray( EMPTY_STRING_ARRAY ) );
1985 }
1986
1987 scanner.addDefaultExcludes();
1988 scanner.scan();
1989
1990 List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );
1991
1992 for ( Iterator<String> j = includedFiles.iterator(); j.hasNext(); )
1993 {
1994 String name = j.next();
1995 String path = sourcePath + '/' + name;
1996
1997
1998 if ( path.startsWith( basePath ) )
1999 {
2000 if ( path.length() == basePath.length() )
2001 {
2002 path = ".";
2003 }
2004 else
2005 {
2006 path = path.substring( basePath.length() + 1 );
2007 }
2008 }
2009
2010
2011
2012 if ( File.separatorChar != '/' )
2013 {
2014 name = name.replace( File.separatorChar, '/' );
2015 path = path.replace( File.separatorChar, '/' );
2016 }
2017
2018
2019 path = name + '=' + path;
2020 if ( targetPath != null )
2021 {
2022 path = targetPath + '/' + path;
2023 }
2024
2025
2026 if ( resource.isFiltering() )
2027 {
2028 path = '{' + path + '}';
2029 }
2030
2031 pathSet.add( path );
2032 }
2033 }
2034 }
2035
2036 StringBuffer resourcePaths = new StringBuffer();
2037 for ( Iterator<String> i = pathSet.iterator(); i.hasNext(); )
2038 {
2039 resourcePaths.append( i.next() );
2040 if ( i.hasNext() )
2041 {
2042 resourcePaths.append( ',' );
2043 }
2044 }
2045
2046 return resourcePaths.toString();
2047 }
2048
2049
2050 protected Collection<Artifact> getEmbeddableArtifacts(MavenProject currentProject, Analyzer analyzer)
2051 throws MojoExecutionException
2052 {
2053 final Collection<Artifact> artifacts;
2054
2055 String embedTransitive = analyzer.getProperty( DependencyEmbedder.EMBED_TRANSITIVE );
2056 if (Boolean.valueOf(embedTransitive))
2057 {
2058
2059 artifacts = currentProject.getArtifacts();
2060 }
2061 else
2062 {
2063
2064 artifacts = currentProject.getDependencyArtifacts();
2065 }
2066
2067 return getSelectedDependencies(artifacts );
2068 }
2069
2070
2071 protected static void addMavenSourcePath( MavenProject currentProject, Analyzer analyzer, Log log )
2072 {
2073
2074 StringBuilder mavenSourcePaths = new StringBuilder();
2075 StringBuilder mavenTestSourcePaths = new StringBuilder();
2076 Map<StringBuilder, List<String>> map = new HashMap<StringBuilder, List<String>>(2);
2077 map.put(mavenSourcePaths, currentProject.getCompileSourceRoots() );
2078 map.put(mavenTestSourcePaths, currentProject.getTestCompileSourceRoots() );
2079 for ( Map.Entry<StringBuilder, List<String>> entry : map.entrySet() )
2080 {
2081 List<String> compileSourceRoots = entry.getValue();
2082 if ( compileSourceRoots != null )
2083 {
2084 StringBuilder sourcePaths = entry.getKey();
2085 for ( Iterator<String> i = compileSourceRoots.iterator(); i.hasNext(); )
2086 {
2087 if ( sourcePaths.length() > 0 )
2088 {
2089 sourcePaths.append( ',' );
2090 }
2091 sourcePaths.append( i.next() );
2092 }
2093 }
2094 }
2095 final String sourcePath = analyzer.getProperty( Analyzer.SOURCEPATH );
2096 if ( sourcePath != null )
2097 {
2098 if ( sourcePath.contains(MAVEN_SOURCES) || sourcePath.contains(MAVEN_TEST_RESOURCES) )
2099 {
2100 String combinedSource = StringUtils.replace( sourcePath, MAVEN_SOURCES, mavenSourcePaths.toString() );
2101 combinedSource = StringUtils.replace( combinedSource, MAVEN_TEST_SOURCES, mavenTestSourcePaths.toString() );
2102 if ( combinedSource.length() > 0 )
2103 {
2104 analyzer.setProperty( Analyzer.SOURCEPATH, combinedSource );
2105 }
2106 else
2107 {
2108 analyzer.unsetProperty( Analyzer.SOURCEPATH );
2109 }
2110 }
2111 else if ( mavenSourcePaths.length() > 0 )
2112 {
2113 log.warn( Analyzer.SOURCEPATH + ": overriding " + mavenSourcePaths + " with " + sourcePath + " (add "
2114 + MAVEN_SOURCES + " if you want to include the maven sources)" );
2115 }
2116 else if ( mavenTestSourcePaths.length() > 0 )
2117 {
2118 log.warn( Analyzer.SOURCEPATH + ": overriding " + mavenTestSourcePaths + " with " + sourcePath + " (add "
2119 + MAVEN_TEST_SOURCES + " if you want to include the maven test sources)" );
2120 }
2121 }
2122 else if ( mavenSourcePaths.length() > 0 )
2123 {
2124 analyzer.setProperty( Analyzer.SOURCEPATH, mavenSourcePaths.toString() );
2125 }
2126 }
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137 protected static void includeJava9Fixups(MavenProject currentProject, Analyzer analyzer)
2138 {
2139 final String classesInWrongDirError = "Classes found in the wrong directory";
2140 final String newFixup = "Classes found in the wrong directory;"
2141 + Analyzer.FIXUPMESSAGES_IS_DIRECTIVE + "="
2142 + Analyzer.FIXUPMESSAGES_IS_WARNING;
2143
2144 String fixups = analyzer.getProperty(Analyzer.FIXUPMESSAGES);
2145 if (fixups != null && !fixups.isEmpty()) {
2146 if (!fixups.contains(classesInWrongDirError)) {
2147 fixups += "," + newFixup;
2148 }
2149 } else {
2150 fixups = newFixup;
2151 }
2152 analyzer.setProperty(Analyzer.FIXUPMESSAGES, fixups);
2153 }
2154
2155 static class ClassPathItem {
2156 final String id;
2157 final File file;
2158
2159 public ClassPathItem(String id, File file) {
2160 this.id = id;
2161 this.file = file;
2162 }
2163 }
2164 }