BundlePlugin xref

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