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