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