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.lang.reflect.Array;
29 import java.lang.reflect.Method;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.Enumeration;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.LinkedHashMap;
38 import java.util.LinkedHashSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Properties;
42 import java.util.Set;
43 import java.util.jar.Attributes;
44 import java.util.jar.Manifest;
45
46 import org.apache.maven.archiver.ManifestSection;
47 import org.apache.maven.archiver.MavenArchiveConfiguration;
48 import org.apache.maven.archiver.MavenArchiver;
49 import org.apache.maven.artifact.Artifact;
50 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
51 import org.apache.maven.execution.MavenSession;
52 import org.apache.maven.model.License;
53 import org.apache.maven.model.Model;
54 import org.apache.maven.model.Resource;
55 import org.apache.maven.plugin.AbstractMojo;
56 import org.apache.maven.plugin.MojoExecutionException;
57 import org.apache.maven.plugin.MojoFailureException;
58 import org.apache.maven.plugin.logging.Log;
59 import org.apache.maven.project.MavenProject;
60 import org.apache.maven.project.MavenProjectHelper;
61 import org.apache.maven.shared.osgi.DefaultMaven2OsgiConverter;
62 import org.apache.maven.shared.osgi.Maven2OsgiConverter;
63 import org.codehaus.plexus.archiver.UnArchiver;
64 import org.codehaus.plexus.archiver.manager.ArchiverManager;
65 import org.codehaus.plexus.util.DirectoryScanner;
66 import org.codehaus.plexus.util.FileUtils;
67 import org.codehaus.plexus.util.PropertyUtils;
68 import org.codehaus.plexus.util.StringUtils;
69
70 import aQute.bnd.header.Attrs;
71 import aQute.bnd.osgi.Analyzer;
72 import aQute.bnd.osgi.Builder;
73 import aQute.bnd.osgi.Constants;
74 import aQute.bnd.osgi.Descriptors.PackageRef;
75 import aQute.bnd.osgi.EmbeddedResource;
76 import aQute.bnd.osgi.FileResource;
77 import aQute.bnd.osgi.Jar;
78 import aQute.bnd.osgi.Packages;
79 import aQute.bnd.osgi.Processor;
80 import aQute.lib.spring.SpringXMLType;
81
82
83
84
85
86
87
88
89
90
91
92 public class BundlePlugin extends AbstractMojo
93 {
94
95
96
97
98
99 protected File manifestLocation;
100
101
102
103
104
105
106 protected File dumpInstructions;
107
108
109
110
111
112
113 protected File dumpClasspath;
114
115
116
117
118
119
120 protected boolean unpackBundle;
121
122
123
124
125
126
127 protected String excludeDependencies;
128
129
130
131
132
133
134 private String finalName;
135
136
137
138
139
140
141
142 protected String classifier;
143
144
145
146
147
148
149
150 protected String packaging;
151
152
153
154
155 private MavenProjectHelper m_projectHelper;
156
157
158
159
160 private ArchiverManager m_archiverManager;
161
162
163
164
165 private ArtifactHandlerManager m_artifactHandlerManager;
166
167
168
169
170
171
172 protected List supportedProjectTypes = Arrays.asList( new String[]
173 { "jar", "bundle" } );
174
175
176
177
178
179
180
181 private File outputDirectory;
182
183
184
185
186
187
188
189 private String buildDirectory;
190
191
192
193
194
195
196
197
198 private MavenProject project;
199
200
201
202
203
204
205 private Map instructions = new LinkedHashMap();
206
207
208
209
210 private Maven2OsgiConverter m_maven2OsgiConverter = new DefaultMaven2OsgiConverter();
211
212
213
214
215
216
217 private MavenArchiveConfiguration archive;
218
219
220
221
222
223
224 private MavenSession m_mavenSession;
225
226 private static final String MAVEN_SYMBOLICNAME = "maven-symbolicname";
227 private static final String MAVEN_RESOURCES = "{maven-resources}";
228 private static final String LOCAL_PACKAGES = "{local-packages}";
229 private static final String MAVEN_SOURCES = "{maven-sources}";
230
231 private static final String[] EMPTY_STRING_ARRAY =
232 {};
233 private static final String[] DEFAULT_INCLUDES =
234 { "**/**" };
235
236 private static final String NL = System.getProperty( "line.separator" );
237
238
239 protected Maven2OsgiConverter getMaven2OsgiConverter()
240 {
241 return m_maven2OsgiConverter;
242 }
243
244
245 protected void setMaven2OsgiConverter( Maven2OsgiConverter maven2OsgiConverter )
246 {
247 m_maven2OsgiConverter = maven2OsgiConverter;
248 }
249
250
251 protected MavenProject getProject()
252 {
253 return project;
254 }
255
256
257
258
259
260 public void execute() throws MojoExecutionException
261 {
262 Properties properties = new Properties();
263 String projectType = getProject().getArtifact().getType();
264
265
266 if ( !supportedProjectTypes.contains( projectType ) )
267 {
268 getLog().warn(
269 "Ignoring project type " + projectType + " - supportedProjectTypes = " + supportedProjectTypes );
270 return;
271 }
272
273 execute( getProject(), instructions, properties );
274 }
275
276
277 protected void execute( MavenProject currentProject, Map originalInstructions, Properties properties )
278 throws MojoExecutionException
279 {
280 try
281 {
282 execute( currentProject, originalInstructions, properties, getClasspath( currentProject ) );
283 }
284 catch ( IOException e )
285 {
286 throw new MojoExecutionException( "Error calculating classpath for project " + currentProject, e );
287 }
288 }
289
290
291
292 protected static Map transformDirectives( Map originalInstructions )
293 {
294 Map transformedInstructions = new LinkedHashMap();
295 for ( Iterator i = originalInstructions.entrySet().iterator(); i.hasNext(); )
296 {
297 Map.Entry e = ( Map.Entry ) i.next();
298
299 String key = ( String ) e.getKey();
300 if ( key.startsWith( "_" ) )
301 {
302 key = "-" + key.substring( 1 );
303 }
304
305 String value = ( String ) e.getValue();
306 if ( null == value )
307 {
308 value = "";
309 }
310 else
311 {
312 value = value.replaceAll( "\\p{Blank}*[\r\n]\\p{Blank}*", "" );
313 }
314
315 if ( Analyzer.WAB.equals( key ) && value.length() == 0 )
316 {
317
318 value = "src/main/webapp/";
319 }
320
321 transformedInstructions.put( key, value );
322 }
323 return transformedInstructions;
324 }
325
326
327 protected boolean reportErrors( String prefix, Analyzer analyzer )
328 {
329 List errors = analyzer.getErrors();
330 List warnings = analyzer.getWarnings();
331
332 for ( Iterator w = warnings.iterator(); w.hasNext(); )
333 {
334 String msg = ( String ) w.next();
335 getLog().warn( prefix + " : " + msg );
336 }
337
338 boolean hasErrors = false;
339 String fileNotFound = "Input file does not exist: ";
340 for ( Iterator e = errors.iterator(); e.hasNext(); )
341 {
342 String msg = ( String ) e.next();
343 if ( msg.startsWith( fileNotFound ) && msg.endsWith( "~" ) )
344 {
345
346 String duplicate = Processor.removeDuplicateMarker( msg.substring( fileNotFound.length() ) );
347 getLog().warn( prefix + " : Duplicate path '" + duplicate + "' in Include-Resource" );
348 }
349 else
350 {
351 getLog().error( prefix + " : " + msg );
352 hasErrors = true;
353 }
354 }
355 return hasErrors;
356 }
357
358
359 protected void execute( MavenProject currentProject, Map originalInstructions, Properties properties,
360 Jar[] classpath ) throws MojoExecutionException
361 {
362 try
363 {
364 File jarFile = new File( getBuildDirectory(), getBundleName( currentProject ) );
365 Builder builder = buildOSGiBundle( currentProject, originalInstructions, properties, classpath );
366 boolean hasErrors = reportErrors( "Bundle " + currentProject.getArtifact(), builder );
367 if ( hasErrors )
368 {
369 String failok = builder.getProperty( "-failok" );
370 if ( null == failok || "false".equalsIgnoreCase( failok ) )
371 {
372 jarFile.delete();
373
374 throw new MojoFailureException( "Error(s) found in bundle configuration" );
375 }
376 }
377
378
379 jarFile.getParentFile().mkdirs();
380 builder.getJar().write( jarFile );
381
382 Artifact mainArtifact = currentProject.getArtifact();
383
384 if ( "bundle".equals( mainArtifact.getType() ) )
385 {
386
387 mainArtifact.setArtifactHandler( m_artifactHandlerManager.getArtifactHandler( "jar" ) );
388 }
389
390 boolean customClassifier = null != classifier && classifier.trim().length() > 0;
391 boolean customPackaging = null != packaging && packaging.trim().length() > 0;
392
393 if ( customClassifier && customPackaging )
394 {
395 m_projectHelper.attachArtifact( currentProject, packaging, classifier, jarFile );
396 }
397 else if ( customClassifier )
398 {
399 m_projectHelper.attachArtifact( currentProject, jarFile, classifier );
400 }
401 else if ( customPackaging )
402 {
403 m_projectHelper.attachArtifact( currentProject, packaging, jarFile );
404 }
405 else
406 {
407 mainArtifact.setFile( jarFile );
408 }
409
410 if ( unpackBundle )
411 {
412 unpackBundle( jarFile );
413 }
414
415 if ( manifestLocation != null )
416 {
417 File outputFile = new File( manifestLocation, "MANIFEST.MF" );
418
419 try
420 {
421 Manifest manifest = builder.getJar().getManifest();
422 ManifestPlugin.writeManifest( manifest, outputFile );
423 }
424 catch ( IOException e )
425 {
426 getLog().error( "Error trying to write Manifest to file " + outputFile, e );
427 }
428 }
429
430
431 builder.close();
432 }
433 catch ( MojoFailureException e )
434 {
435 getLog().error( e.getLocalizedMessage() );
436 throw new MojoExecutionException( "Error(s) found in bundle configuration", e );
437 }
438 catch ( Exception e )
439 {
440 getLog().error( "An internal error occurred", e );
441 throw new MojoExecutionException( "Internal error in maven-bundle-plugin", e );
442 }
443 }
444
445
446 protected Builder getOSGiBuilder( MavenProject currentProject, Map originalInstructions, Properties properties,
447 Jar[] classpath ) throws Exception
448 {
449 properties.putAll( getDefaultProperties( currentProject ) );
450 properties.putAll( transformDirectives( originalInstructions ) );
451
452 Builder builder = new Builder();
453 synchronized ( BundlePlugin.class )
454 {
455 builder.setBase( getBase( currentProject ) );
456 }
457 builder.setProperties( sanitize( properties ) );
458 if ( classpath != null )
459 {
460 builder.setClasspath( classpath );
461 }
462
463 return builder;
464 }
465
466
467 protected static Properties sanitize( Properties properties )
468 {
469
470 Properties sanitizedEntries = new Properties();
471 for ( Iterator itr = properties.entrySet().iterator(); itr.hasNext(); )
472 {
473 Map.Entry entry = ( Map.Entry ) itr.next();
474 if ( entry.getKey() instanceof String == false )
475 {
476 String key = sanitize( entry.getKey() );
477 if ( !properties.containsKey( key ) )
478 {
479 sanitizedEntries.setProperty( key, sanitize( entry.getValue() ) );
480 }
481 itr.remove();
482 }
483 else if ( entry.getValue() instanceof String == false )
484 {
485 entry.setValue( sanitize( entry.getValue() ) );
486 }
487 }
488 properties.putAll( sanitizedEntries );
489 return properties;
490 }
491
492
493 protected static String sanitize( Object value )
494 {
495 if ( value instanceof String )
496 {
497 return ( String ) value;
498 }
499 else if ( value instanceof Iterable )
500 {
501 String delim = "";
502 StringBuilder buf = new StringBuilder();
503 for ( Object i : ( Iterable<?> ) value )
504 {
505 buf.append( delim ).append( i );
506 delim = ", ";
507 }
508 return buf.toString();
509 }
510 else if ( value.getClass().isArray() )
511 {
512 String delim = "";
513 StringBuilder buf = new StringBuilder();
514 for ( int i = 0, len = Array.getLength( value ); i < len; i++ )
515 {
516 buf.append( delim ).append( Array.get( value, i ) );
517 delim = ", ";
518 }
519 return buf.toString();
520 }
521 else
522 {
523 return String.valueOf( value );
524 }
525 }
526
527
528 protected void addMavenInstructions( MavenProject currentProject, Builder builder ) throws Exception
529 {
530 if ( currentProject.getBasedir() != null )
531 {
532
533 includeMavenResources( currentProject, builder, getLog() );
534
535
536 addLocalPackages( outputDirectory, builder );
537
538
539 addMavenSourcePath( currentProject, builder, getLog() );
540 }
541
542
543 Collection embeddableArtifacts = getEmbeddableArtifacts( currentProject, builder );
544 new DependencyEmbedder( getLog(), embeddableArtifacts ).processHeaders( builder );
545
546 if ( dumpInstructions != null || getLog().isDebugEnabled() )
547 {
548 StringBuilder buf = new StringBuilder();
549 getLog().debug( "BND Instructions:" + NL + dumpInstructions( builder.getProperties(), buf ) );
550 if ( dumpInstructions != null )
551 {
552 getLog().info( "Writing BND instructions to " + dumpInstructions );
553 dumpInstructions.getParentFile().mkdirs();
554 FileUtils.fileWrite( dumpInstructions, "# BND instructions" + NL + buf );
555 }
556 }
557
558 if ( dumpClasspath != null || getLog().isDebugEnabled() )
559 {
560 StringBuilder buf = new StringBuilder();
561 getLog().debug( "BND Classpath:" + NL + dumpClasspath( builder.getClasspath(), buf ) );
562 if ( dumpClasspath != null )
563 {
564 getLog().info( "Writing BND classpath to " + dumpClasspath );
565 dumpClasspath.getParentFile().mkdirs();
566 FileUtils.fileWrite( dumpClasspath, "# BND classpath" + NL + buf );
567 }
568 }
569 }
570
571
572 protected Builder buildOSGiBundle( MavenProject currentProject, Map originalInstructions, Properties properties,
573 Jar[] classpath ) throws Exception
574 {
575 Builder builder = getOSGiBuilder( currentProject, originalInstructions, properties, classpath );
576
577 addMavenInstructions( currentProject, builder );
578
579 builder.build();
580
581 mergeMavenManifest( currentProject, builder );
582
583 return builder;
584 }
585
586
587 protected static StringBuilder dumpInstructions( Properties properties, StringBuilder buf )
588 {
589 try
590 {
591 buf.append( "#-----------------------------------------------------------------------" + NL );
592 Properties stringProperties = new Properties();
593 for ( Enumeration e = properties.propertyNames(); e.hasMoreElements(); )
594 {
595
596 String key = ( String ) e.nextElement();
597 String value = properties.getProperty( key );
598 if ( value != null )
599 {
600 stringProperties.setProperty( key, value );
601 }
602 }
603 ByteArrayOutputStream out = new ByteArrayOutputStream();
604 stringProperties.store( out, null );
605 buf.append( out.toString( "8859_1" ) );
606 buf.append( "#-----------------------------------------------------------------------" + NL );
607 }
608 catch ( Throwable e )
609 {
610
611 }
612 return buf;
613 }
614
615
616 protected static StringBuilder dumpClasspath( List classpath, StringBuilder buf )
617 {
618 try
619 {
620 buf.append( "#-----------------------------------------------------------------------" + NL );
621 buf.append( "-classpath:\\" + NL );
622 for ( Iterator i = classpath.iterator(); i.hasNext(); )
623 {
624 File path = ( ( Jar ) i.next() ).getSource();
625 if ( path != null )
626 {
627 buf.append( ' ' + path.toString() + ( i.hasNext() ? ",\\" : "" ) + NL );
628 }
629 }
630 buf.append( "#-----------------------------------------------------------------------" + NL );
631 }
632 catch ( Throwable e )
633 {
634
635 }
636 return buf;
637 }
638
639
640 protected static StringBuilder dumpManifest( Manifest manifest, StringBuilder buf )
641 {
642 try
643 {
644 buf.append( "#-----------------------------------------------------------------------" + NL );
645 ByteArrayOutputStream out = new ByteArrayOutputStream();
646 Jar.writeManifest( manifest, out );
647 buf.append( out.toString( "UTF8" ) );
648 buf.append( "#-----------------------------------------------------------------------" + NL );
649 }
650 catch ( Throwable e )
651 {
652
653 }
654 return buf;
655 }
656
657
658 protected static void includeMavenResources( MavenProject currentProject, Analyzer analyzer, Log log )
659 {
660
661 final String mavenResourcePaths = getMavenResourcePaths( currentProject );
662 final String includeResource = ( String ) analyzer.getProperty( Analyzer.INCLUDE_RESOURCE );
663 if ( includeResource != null )
664 {
665 if ( includeResource.indexOf( MAVEN_RESOURCES ) >= 0 )
666 {
667
668
669 if ( mavenResourcePaths.length() == 0 )
670 {
671 String cleanedResource = removeTagFromInstruction( includeResource, MAVEN_RESOURCES );
672 if ( cleanedResource.length() > 0 )
673 {
674 analyzer.setProperty( Analyzer.INCLUDE_RESOURCE, cleanedResource );
675 }
676 else
677 {
678 analyzer.unsetProperty( Analyzer.INCLUDE_RESOURCE );
679 }
680 }
681 else
682 {
683 String combinedResource = StringUtils
684 .replace( includeResource, MAVEN_RESOURCES, mavenResourcePaths );
685 analyzer.setProperty( Analyzer.INCLUDE_RESOURCE, combinedResource );
686 }
687 }
688 else if ( mavenResourcePaths.length() > 0 )
689 {
690 log.warn( Analyzer.INCLUDE_RESOURCE + ": overriding " + mavenResourcePaths + " with " + includeResource
691 + " (add " + MAVEN_RESOURCES + " if you want to include the maven resources)" );
692 }
693 }
694 else if ( mavenResourcePaths.length() > 0 )
695 {
696 analyzer.setProperty( Analyzer.INCLUDE_RESOURCE, mavenResourcePaths );
697 }
698 }
699
700
701 protected void mergeMavenManifest( MavenProject currentProject, Builder builder ) throws Exception
702 {
703 Jar jar = builder.getJar();
704
705 if ( getLog().isDebugEnabled() )
706 {
707 getLog().debug( "BND Manifest:" + NL + dumpManifest( jar.getManifest(), new StringBuilder() ) );
708 }
709
710 boolean addMavenDescriptor = currentProject.getBasedir() != null;
711
712 try
713 {
714
715
716
717 MavenArchiveConfiguration archiveConfig = JarPluginConfiguration.getArchiveConfiguration( currentProject );
718 String mavenManifestText = new MavenArchiver().getManifest( currentProject, archiveConfig ).toString();
719 addMavenDescriptor = addMavenDescriptor && archiveConfig.isAddMavenDescriptor();
720
721 Manifest mavenManifest = new Manifest();
722
723
724 File externalManifestFile = archiveConfig.getManifestFile();
725 if ( null != externalManifestFile )
726 {
727 if ( !externalManifestFile.isAbsolute() )
728 {
729 externalManifestFile = new File( currentProject.getBasedir(), externalManifestFile.getPath() );
730 }
731 if ( externalManifestFile.exists() && !externalManifestFile.equals( new File( manifestLocation, "MANIFEST.MF" ) ) )
732 {
733 InputStream mis = new FileInputStream( externalManifestFile );
734 mavenManifest.read( mis );
735 mis.close();
736 }
737 }
738
739
740 mavenManifest.read( new ByteArrayInputStream( mavenManifestText.getBytes( "UTF8" ) ) );
741
742 if ( !archiveConfig.isManifestSectionsEmpty() )
743 {
744
745
746
747 List sections = archiveConfig.getManifestSections();
748 for ( Iterator i = sections.iterator(); i.hasNext(); )
749 {
750 ManifestSection section = ( ManifestSection ) i.next();
751 Attributes attributes = new Attributes();
752
753 if ( !section.isManifestEntriesEmpty() )
754 {
755 Map entries = section.getManifestEntries();
756 for ( Iterator j = entries.entrySet().iterator(); j.hasNext(); )
757 {
758 Map.Entry entry = ( Map.Entry ) j.next();
759 attributes.putValue( ( String ) entry.getKey(), ( String ) entry.getValue() );
760 }
761 }
762
763 mavenManifest.getEntries().put( section.getName(), attributes );
764 }
765 }
766
767 Attributes mainMavenAttributes = mavenManifest.getMainAttributes();
768 mainMavenAttributes.putValue( "Created-By", "Apache Maven Bundle Plugin" );
769
770 String[] removeHeaders = builder.getProperty( Constants.REMOVEHEADERS, "" ).split( "," );
771
772
773 for ( int i = 0; i < removeHeaders.length; i++ )
774 {
775 for ( Iterator j = mainMavenAttributes.keySet().iterator(); j.hasNext(); )
776 {
777 if ( j.next().toString().matches( removeHeaders[i].trim() ) )
778 {
779 j.remove();
780 }
781 }
782 }
783
784
785
786
787 Manifest bundleManifest = jar.getManifest();
788 bundleManifest.getMainAttributes().putAll( mainMavenAttributes );
789 bundleManifest.getEntries().putAll( mavenManifest.getEntries() );
790
791
792
793 String importPackages = bundleManifest.getMainAttributes().getValue( "Import-Package" );
794 if ( importPackages != null )
795 {
796 Set optionalPackages = getOptionalPackages( currentProject );
797
798 Map<String, ? extends Map<String, String>> values = new Analyzer().parseHeader( importPackages );
799 for ( Map.Entry<String, ? extends Map<String, String>> entry : values.entrySet() )
800 {
801 String pkg = entry.getKey();
802 Map<String, String> options = entry.getValue();
803 if ( !options.containsKey( "resolution:" ) && optionalPackages.contains( pkg ) )
804 {
805 options.put( "resolution:", "optional" );
806 }
807 }
808 String result = Processor.printClauses( values );
809 bundleManifest.getMainAttributes().putValue( "Import-Package", result );
810 }
811
812 jar.setManifest( bundleManifest );
813 }
814 catch ( Exception e )
815 {
816 getLog().warn( "Unable to merge Maven manifest: " + e.getLocalizedMessage() );
817 }
818
819 if ( addMavenDescriptor )
820 {
821 doMavenMetadata( currentProject, jar );
822 }
823
824 if ( getLog().isDebugEnabled() )
825 {
826 getLog().debug( "Final Manifest:" + NL + dumpManifest( jar.getManifest(), new StringBuilder() ) );
827 }
828
829 builder.setJar( jar );
830 }
831
832
833 protected Set getOptionalPackages( MavenProject currentProject ) throws IOException, MojoExecutionException
834 {
835 ArrayList inscope = new ArrayList();
836 final Collection artifacts = getSelectedDependencies( currentProject.getArtifacts() );
837 for ( Iterator it = artifacts.iterator(); it.hasNext(); )
838 {
839 Artifact artifact = ( Artifact ) it.next();
840 if ( artifact.getArtifactHandler().isAddedToClasspath() )
841 {
842 if ( !Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
843 {
844 inscope.add( artifact );
845 }
846 }
847 }
848
849 HashSet optionalArtifactIds = new HashSet();
850 for ( Iterator it = inscope.iterator(); it.hasNext(); )
851 {
852 Artifact artifact = ( Artifact ) it.next();
853 if ( artifact.isOptional() )
854 {
855 String id = artifact.toString();
856 if ( artifact.getScope() != null )
857 {
858
859 id = id.replaceFirst( ":[^:]*$", "" );
860 }
861 optionalArtifactIds.add( id );
862 }
863
864 }
865
866 HashSet required = new HashSet();
867 HashSet optional = new HashSet();
868 for ( Iterator it = inscope.iterator(); it.hasNext(); )
869 {
870 Artifact artifact = ( Artifact ) it.next();
871 File file = getFile( artifact );
872 if ( file == null )
873 {
874 continue;
875 }
876
877 Jar jar = new Jar( artifact.getArtifactId(), file );
878 if ( isTransitivelyOptional( optionalArtifactIds, artifact ) )
879 {
880 optional.addAll( jar.getPackages() );
881 }
882 else
883 {
884 required.addAll( jar.getPackages() );
885 }
886 jar.close();
887 }
888
889 optional.removeAll( required );
890 return optional;
891 }
892
893
894
895
896
897
898
899
900 protected boolean isTransitivelyOptional( HashSet optionalArtifactIds, Artifact artifact )
901 {
902 List trail = artifact.getDependencyTrail();
903 for ( Iterator iterator = trail.iterator(); iterator.hasNext(); )
904 {
905 String next = ( String ) iterator.next();
906 if ( optionalArtifactIds.contains( next ) )
907 {
908 return true;
909 }
910 }
911 return false;
912 }
913
914
915 private void unpackBundle( File jarFile )
916 {
917 File outputDir = getOutputDirectory();
918 if ( null == outputDir )
919 {
920 outputDir = new File( getBuildDirectory(), "classes" );
921 }
922
923 try
924 {
925
926
927
928
929 if ( !outputDir.exists() )
930 {
931 outputDir.mkdirs();
932 }
933
934 UnArchiver unArchiver = m_archiverManager.getUnArchiver( "jar" );
935 unArchiver.setDestDirectory( outputDir );
936 unArchiver.setSourceFile( jarFile );
937 unArchiver.extract();
938 }
939 catch ( Exception e )
940 {
941 getLog().error( "Problem unpacking " + jarFile + " to " + outputDir, e );
942 }
943 }
944
945
946 protected static String removeTagFromInstruction( String instruction, String tag )
947 {
948 StringBuffer buf = new StringBuffer();
949
950 String[] clauses = instruction.split( "," );
951 for ( int i = 0; i < clauses.length; i++ )
952 {
953 String clause = clauses[i].trim();
954 if ( !tag.equals( clause ) )
955 {
956 if ( buf.length() > 0 )
957 {
958 buf.append( ',' );
959 }
960 buf.append( clause );
961 }
962 }
963
964 return buf.toString();
965 }
966
967
968 private static Map getProperties( Model projectModel, String prefix )
969 {
970 Map properties = new LinkedHashMap();
971 Method methods[] = Model.class.getDeclaredMethods();
972 for ( int i = 0; i < methods.length; i++ )
973 {
974 String name = methods[i].getName();
975 if ( name.startsWith( "get" ) )
976 {
977 try
978 {
979 Object v = methods[i].invoke( projectModel, null );
980 if ( v != null )
981 {
982 name = prefix + Character.toLowerCase( name.charAt( 3 ) ) + name.substring( 4 );
983 if ( v.getClass().isArray() )
984 properties.put( name, Arrays.asList( ( Object[] ) v ).toString() );
985 else
986 properties.put( name, v );
987
988 }
989 }
990 catch ( Exception e )
991 {
992
993 }
994 }
995 }
996 return properties;
997 }
998
999
1000 private static StringBuffer printLicenses( List licenses )
1001 {
1002 if ( licenses == null || licenses.size() == 0 )
1003 return null;
1004 StringBuffer sb = new StringBuffer();
1005 String del = "";
1006 for ( Iterator i = licenses.iterator(); i.hasNext(); )
1007 {
1008 License l = ( License ) i.next();
1009 String url = l.getUrl();
1010 if ( url == null )
1011 continue;
1012 sb.append( del );
1013 sb.append( url );
1014 del = ", ";
1015 }
1016 if ( sb.length() == 0 )
1017 return null;
1018 return sb;
1019 }
1020
1021
1022
1023
1024
1025
1026 private void doMavenMetadata( MavenProject currentProject, Jar jar ) throws IOException
1027 {
1028 String path = "META-INF/maven/" + currentProject.getGroupId() + "/" + currentProject.getArtifactId();
1029 File pomFile = new File( currentProject.getBasedir(), "pom.xml" );
1030 jar.putResource( path + "/pom.xml", new FileResource( pomFile ) );
1031
1032 Properties p = new Properties();
1033 p.put( "version", currentProject.getVersion() );
1034 p.put( "groupId", currentProject.getGroupId() );
1035 p.put( "artifactId", currentProject.getArtifactId() );
1036 ByteArrayOutputStream out = new ByteArrayOutputStream();
1037 p.store( out, "Generated by org.apache.felix.bundleplugin" );
1038 jar.putResource( path + "/pom.properties", new EmbeddedResource( out.toByteArray(), System.currentTimeMillis() ) );
1039 }
1040
1041
1042 protected Jar[] getClasspath( MavenProject currentProject ) throws IOException, MojoExecutionException
1043 {
1044 List list = new ArrayList();
1045
1046 if ( getOutputDirectory() != null && getOutputDirectory().exists() )
1047 {
1048 list.add( new Jar( ".", getOutputDirectory() ) );
1049 }
1050
1051 final Collection artifacts = getSelectedDependencies( currentProject.getArtifacts() );
1052 for ( Iterator it = artifacts.iterator(); it.hasNext(); )
1053 {
1054 Artifact artifact = ( Artifact ) it.next();
1055 if ( artifact.getArtifactHandler().isAddedToClasspath() )
1056 {
1057 if ( !Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
1058 {
1059 File file = getFile( artifact );
1060 if ( file == null )
1061 {
1062 getLog().warn(
1063 "File is not available for artifact " + artifact + " in project "
1064 + currentProject.getArtifact() );
1065 continue;
1066 }
1067 Jar jar = new Jar( artifact.getArtifactId(), file );
1068 list.add( jar );
1069 }
1070 }
1071 }
1072 Jar[] cp = new Jar[list.size()];
1073 list.toArray( cp );
1074 return cp;
1075 }
1076
1077
1078 private Collection getSelectedDependencies( Collection artifacts ) throws MojoExecutionException
1079 {
1080 if ( null == excludeDependencies || excludeDependencies.length() == 0 )
1081 {
1082 return artifacts;
1083 }
1084 else if ( "true".equalsIgnoreCase( excludeDependencies ) )
1085 {
1086 return Collections.EMPTY_LIST;
1087 }
1088
1089 Collection selectedDependencies = new LinkedHashSet( artifacts );
1090 DependencyExcluder excluder = new DependencyExcluder( artifacts );
1091 excluder.processHeaders( excludeDependencies );
1092 selectedDependencies.removeAll( excluder.getExcludedArtifacts() );
1093
1094 return selectedDependencies;
1095 }
1096
1097
1098
1099
1100
1101
1102
1103 protected File getFile( Artifact artifact )
1104 {
1105 return artifact.getFile();
1106 }
1107
1108
1109 private static void header( Properties properties, String key, Object value )
1110 {
1111 if ( value == null )
1112 return;
1113
1114 if ( value instanceof Collection && ( ( Collection ) value ).isEmpty() )
1115 return;
1116
1117 properties.put( key, value.toString().replaceAll( "[\r\n]", "" ) );
1118 }
1119
1120
1121
1122
1123
1124
1125
1126
1127 protected String convertVersionToOsgi( String version )
1128 {
1129 return getMaven2OsgiConverter().getVersion( version );
1130 }
1131
1132
1133
1134
1135
1136 protected String getBundleName( MavenProject currentProject )
1137 {
1138 String extension;
1139 try
1140 {
1141 extension = currentProject.getArtifact().getArtifactHandler().getExtension();
1142 }
1143 catch ( Throwable e )
1144 {
1145 extension = currentProject.getArtifact().getType();
1146 }
1147 if ( StringUtils.isEmpty( extension ) || "bundle".equals( extension ) || "pom".equals( extension ) )
1148 {
1149 extension = "jar";
1150 }
1151 if ( null != classifier && classifier.trim().length() > 0 )
1152 {
1153 return finalName + '-' + classifier + '.' + extension;
1154 }
1155 return finalName + '.' + extension;
1156 }
1157
1158
1159 protected String getBuildDirectory()
1160 {
1161 return buildDirectory;
1162 }
1163
1164
1165 protected void setBuildDirectory( String _buildirectory )
1166 {
1167 buildDirectory = _buildirectory;
1168 }
1169
1170
1171 protected Properties getDefaultProperties( MavenProject currentProject )
1172 {
1173 Properties properties = new Properties();
1174
1175 String bsn;
1176 try
1177 {
1178 bsn = getMaven2OsgiConverter().getBundleSymbolicName( currentProject.getArtifact() );
1179 }
1180 catch ( Exception e )
1181 {
1182 bsn = currentProject.getGroupId() + "." + currentProject.getArtifactId();
1183 }
1184
1185
1186 properties.put( MAVEN_SYMBOLICNAME, bsn );
1187 properties.put( Analyzer.BUNDLE_SYMBOLICNAME, bsn );
1188 properties.put( Analyzer.IMPORT_PACKAGE, "*" );
1189 properties.put( Analyzer.BUNDLE_VERSION, getMaven2OsgiConverter().getVersion( currentProject.getVersion() ) );
1190
1191
1192 properties.put( Constants.REMOVEHEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE );
1193
1194 header( properties, Analyzer.BUNDLE_DESCRIPTION, currentProject.getDescription() );
1195 StringBuffer licenseText = printLicenses( currentProject.getLicenses() );
1196 if ( licenseText != null )
1197 {
1198 header( properties, Analyzer.BUNDLE_LICENSE, licenseText );
1199 }
1200 header( properties, Analyzer.BUNDLE_NAME, currentProject.getName() );
1201
1202 if ( currentProject.getOrganization() != null )
1203 {
1204 if ( currentProject.getOrganization().getName() != null )
1205 {
1206 String organizationName = currentProject.getOrganization().getName();
1207 header( properties, Analyzer.BUNDLE_VENDOR, organizationName );
1208 properties.put( "project.organization.name", organizationName );
1209 properties.put( "pom.organization.name", organizationName );
1210 }
1211 if ( currentProject.getOrganization().getUrl() != null )
1212 {
1213 String organizationUrl = currentProject.getOrganization().getUrl();
1214 header( properties, Analyzer.BUNDLE_DOCURL, organizationUrl );
1215 properties.put( "project.organization.url", organizationUrl );
1216 properties.put( "pom.organization.url", organizationUrl );
1217 }
1218 }
1219
1220 properties.putAll( currentProject.getProperties() );
1221 properties.putAll( currentProject.getModel().getProperties() );
1222
1223 for ( Iterator i = currentProject.getFilters().iterator(); i.hasNext(); )
1224 {
1225 File filterFile = new File( ( String ) i.next() );
1226 if ( filterFile.isFile() )
1227 {
1228 properties.putAll( PropertyUtils.loadProperties( filterFile ) );
1229 }
1230 }
1231
1232 if ( m_mavenSession != null )
1233 {
1234 try
1235 {
1236
1237 Properties sessionProperties = m_mavenSession.getExecutionProperties();
1238 for ( Enumeration e = sessionProperties.propertyNames(); e.hasMoreElements(); )
1239 {
1240 String key = ( String ) e.nextElement();
1241 if ( key.length() > 0 && !Character.isUpperCase( key.charAt( 0 ) ) )
1242 {
1243 properties.put( key, sessionProperties.getProperty( key ) );
1244 }
1245 }
1246 }
1247 catch ( Exception e )
1248 {
1249 getLog().warn( "Problem with Maven session properties: " + e.getLocalizedMessage() );
1250 }
1251 }
1252
1253 properties.putAll( getProperties( currentProject.getModel(), "project.build." ) );
1254 properties.putAll( getProperties( currentProject.getModel(), "pom." ) );
1255 properties.putAll( getProperties( currentProject.getModel(), "project." ) );
1256
1257 properties.put( "project.baseDir", getBase( currentProject ) );
1258 properties.put( "project.build.directory", getBuildDirectory() );
1259 properties.put( "project.build.outputdirectory", getOutputDirectory() );
1260
1261 properties.put( "classifier", classifier == null ? "" : classifier );
1262
1263
1264 header( properties, Analyzer.PLUGIN, BlueprintPlugin.class.getName() + "," + SpringXMLType.class.getName() );
1265
1266 return properties;
1267 }
1268
1269
1270 protected static File getBase( MavenProject currentProject )
1271 {
1272 return currentProject.getBasedir() != null ? currentProject.getBasedir() : new File( "" );
1273 }
1274
1275
1276 protected File getOutputDirectory()
1277 {
1278 return outputDirectory;
1279 }
1280
1281
1282 protected void setOutputDirectory( File _outputDirectory )
1283 {
1284 outputDirectory = _outputDirectory;
1285 }
1286
1287
1288 private static void addLocalPackages( File outputDirectory, Analyzer analyzer ) throws IOException
1289 {
1290 Packages packages = new Packages();
1291
1292 if ( outputDirectory != null && outputDirectory.isDirectory() )
1293 {
1294
1295 DirectoryScanner scanner = new DirectoryScanner();
1296 scanner.setBasedir( outputDirectory );
1297 scanner.setIncludes( new String[]
1298 { "**/*.class" } );
1299
1300 scanner.addDefaultExcludes();
1301 scanner.scan();
1302
1303 String[] paths = scanner.getIncludedFiles();
1304 for ( int i = 0; i < paths.length; i++ )
1305 {
1306 packages.put( analyzer.getPackageRef( getPackageName( paths[i] ) ) );
1307 }
1308 }
1309
1310 Packages exportedPkgs = new Packages();
1311 Packages privatePkgs = new Packages();
1312
1313 boolean noprivatePackages = "!*".equals( analyzer.getProperty( Analyzer.PRIVATE_PACKAGE ) );
1314
1315 for ( PackageRef pkg : packages.keySet() )
1316 {
1317
1318 privatePkgs.put( pkg );
1319
1320
1321 String fqn = pkg.getFQN();
1322 if ( noprivatePackages || !( ".".equals( fqn ) || fqn.contains( ".internal" ) || fqn.contains( ".impl" ) ) )
1323 {
1324 exportedPkgs.put( pkg );
1325 }
1326 }
1327
1328 Properties properties = analyzer.getProperties();
1329 String exported = properties.getProperty( Analyzer.EXPORT_PACKAGE );
1330 if ( exported == null )
1331 {
1332 if ( !properties.containsKey( Analyzer.EXPORT_CONTENTS ) )
1333 {
1334
1335 for ( Attrs attrs : exportedPkgs.values() )
1336 {
1337 attrs.put( Constants.SPLIT_PACKAGE_DIRECTIVE, "merge-first" );
1338 }
1339 properties.setProperty( Analyzer.EXPORT_PACKAGE, Processor.printClauses( exportedPkgs ) );
1340 }
1341 else
1342 {
1343
1344 properties.setProperty( Analyzer.EXPORT_PACKAGE, "" );
1345 }
1346 }
1347 else if ( exported.indexOf( LOCAL_PACKAGES ) >= 0 )
1348 {
1349 String newExported = StringUtils.replace( exported, LOCAL_PACKAGES, Processor.printClauses( exportedPkgs ) );
1350 properties.setProperty( Analyzer.EXPORT_PACKAGE, newExported );
1351 }
1352
1353 String internal = properties.getProperty( Analyzer.PRIVATE_PACKAGE );
1354 if ( internal == null )
1355 {
1356 if ( !privatePkgs.isEmpty() )
1357 {
1358 for ( Attrs attrs : privatePkgs.values() )
1359 {
1360 attrs.put( Constants.SPLIT_PACKAGE_DIRECTIVE, "merge-first" );
1361 }
1362 properties.setProperty( Analyzer.PRIVATE_PACKAGE, Processor.printClauses( privatePkgs ) );
1363 }
1364 else
1365 {
1366
1367 properties.setProperty( Analyzer.PRIVATE_PACKAGE, "!*" );
1368 }
1369 }
1370 else if ( internal.indexOf( LOCAL_PACKAGES ) >= 0 )
1371 {
1372 String newInternal = StringUtils.replace( internal, LOCAL_PACKAGES, Processor.printClauses( privatePkgs ) );
1373 properties.setProperty( Analyzer.PRIVATE_PACKAGE, newInternal );
1374 }
1375 }
1376
1377
1378 private static String getPackageName( String filename )
1379 {
1380 int n = filename.lastIndexOf( File.separatorChar );
1381 return n < 0 ? "." : filename.substring( 0, n ).replace( File.separatorChar, '.' );
1382 }
1383
1384
1385 private static List getMavenResources( MavenProject currentProject )
1386 {
1387 List resources = new ArrayList( currentProject.getResources() );
1388
1389 if ( currentProject.getCompileSourceRoots() != null )
1390 {
1391
1392 List packageInfoIncludes = Collections.singletonList( "**/packageinfo" );
1393 for ( Iterator i = currentProject.getCompileSourceRoots().iterator(); i.hasNext(); )
1394 {
1395 String sourceRoot = ( String ) i.next();
1396 Resource packageInfoResource = new Resource();
1397 packageInfoResource.setDirectory( sourceRoot );
1398 packageInfoResource.setIncludes( packageInfoIncludes );
1399 resources.add( packageInfoResource );
1400 }
1401 }
1402
1403 return resources;
1404 }
1405
1406
1407 protected static String getMavenResourcePaths( MavenProject currentProject )
1408 {
1409 final String basePath = currentProject.getBasedir().getAbsolutePath();
1410
1411 Set pathSet = new LinkedHashSet();
1412 for ( Iterator i = getMavenResources( currentProject ).iterator(); i.hasNext(); )
1413 {
1414 Resource resource = ( Resource ) i.next();
1415
1416 final String sourcePath = resource.getDirectory();
1417 final String targetPath = resource.getTargetPath();
1418
1419
1420 if ( new File( sourcePath ).exists() && ( ( targetPath == null ) || ( targetPath.indexOf( ".." ) < 0 ) ) )
1421 {
1422 DirectoryScanner scanner = new DirectoryScanner();
1423
1424 scanner.setBasedir( sourcePath );
1425 if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() )
1426 {
1427 scanner.setIncludes( ( String[] ) resource.getIncludes().toArray( EMPTY_STRING_ARRAY ) );
1428 }
1429 else
1430 {
1431 scanner.setIncludes( DEFAULT_INCLUDES );
1432 }
1433
1434 if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() )
1435 {
1436 scanner.setExcludes( ( String[] ) resource.getExcludes().toArray( EMPTY_STRING_ARRAY ) );
1437 }
1438
1439 scanner.addDefaultExcludes();
1440 scanner.scan();
1441
1442 List includedFiles = Arrays.asList( scanner.getIncludedFiles() );
1443
1444 for ( Iterator j = includedFiles.iterator(); j.hasNext(); )
1445 {
1446 String name = ( String ) j.next();
1447 String path = sourcePath + '/' + name;
1448
1449
1450 if ( path.startsWith( basePath ) )
1451 {
1452 if ( path.length() == basePath.length() )
1453 {
1454 path = ".";
1455 }
1456 else
1457 {
1458 path = path.substring( basePath.length() + 1 );
1459 }
1460 }
1461
1462
1463
1464 if ( File.separatorChar != '/' )
1465 {
1466 name = name.replace( File.separatorChar, '/' );
1467 path = path.replace( File.separatorChar, '/' );
1468 }
1469
1470
1471 path = name + '=' + path;
1472 if ( targetPath != null )
1473 {
1474 path = targetPath + '/' + path;
1475 }
1476
1477
1478 if ( resource.isFiltering() )
1479 {
1480 path = '{' + path + '}';
1481 }
1482
1483 pathSet.add( path );
1484 }
1485 }
1486 }
1487
1488 StringBuffer resourcePaths = new StringBuffer();
1489 for ( Iterator i = pathSet.iterator(); i.hasNext(); )
1490 {
1491 resourcePaths.append( i.next() );
1492 if ( i.hasNext() )
1493 {
1494 resourcePaths.append( ',' );
1495 }
1496 }
1497
1498 return resourcePaths.toString();
1499 }
1500
1501
1502 protected Collection getEmbeddableArtifacts( MavenProject currentProject, Analyzer analyzer )
1503 throws MojoExecutionException
1504 {
1505 final Collection artifacts;
1506
1507 String embedTransitive = analyzer.getProperty( DependencyEmbedder.EMBED_TRANSITIVE );
1508 if ( Boolean.valueOf( embedTransitive ).booleanValue() )
1509 {
1510
1511 artifacts = currentProject.getArtifacts();
1512 }
1513 else
1514 {
1515
1516 artifacts = currentProject.getDependencyArtifacts();
1517 }
1518
1519 return getSelectedDependencies( artifacts );
1520 }
1521
1522
1523 protected static void addMavenSourcePath( MavenProject currentProject, Analyzer analyzer, Log log )
1524 {
1525
1526 StringBuilder mavenSourcePaths = new StringBuilder();
1527 if ( currentProject.getCompileSourceRoots() != null )
1528 {
1529 for ( Iterator i = currentProject.getCompileSourceRoots().iterator(); i.hasNext(); )
1530 {
1531 if ( mavenSourcePaths.length() > 0 )
1532 {
1533 mavenSourcePaths.append( ',' );
1534 }
1535 mavenSourcePaths.append( ( String ) i.next() );
1536 }
1537 }
1538 final String sourcePath = ( String ) analyzer.getProperty( Analyzer.SOURCEPATH );
1539 if ( sourcePath != null )
1540 {
1541 if ( sourcePath.indexOf( MAVEN_SOURCES ) >= 0 )
1542 {
1543
1544
1545 if ( mavenSourcePaths.length() == 0 )
1546 {
1547 String cleanedSource = removeTagFromInstruction( sourcePath, MAVEN_SOURCES );
1548 if ( cleanedSource.length() > 0 )
1549 {
1550 analyzer.setProperty( Analyzer.SOURCEPATH, cleanedSource );
1551 }
1552 else
1553 {
1554 analyzer.unsetProperty( Analyzer.SOURCEPATH );
1555 }
1556 }
1557 else
1558 {
1559 String combinedSource = StringUtils
1560 .replace( sourcePath, MAVEN_SOURCES, mavenSourcePaths.toString() );
1561 analyzer.setProperty( Analyzer.SOURCEPATH, combinedSource );
1562 }
1563 }
1564 else if ( mavenSourcePaths.length() > 0 )
1565 {
1566 log.warn( Analyzer.SOURCEPATH + ": overriding " + mavenSourcePaths + " with " + sourcePath + " (add "
1567 + MAVEN_SOURCES + " if you want to include the maven sources)" );
1568 }
1569 }
1570 else if ( mavenSourcePaths.length() > 0 )
1571 {
1572 analyzer.setProperty( Analyzer.SOURCEPATH, mavenSourcePaths.toString() );
1573 }
1574 }
1575 }