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