DependencyEmbedder 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.File;
    23  import java.util.Collection;
    24  import java.util.Iterator;
    25  import java.util.LinkedHashSet;
    26  import java.util.Map;
    27
    28  import org.apache.maven.artifact.Artifact;
    29  import org.apache.maven.plugin.MojoExecutionException;
    30  import org.apache.maven.plugin.logging.Log;
    31  import org.codehaus.plexus.util.StringUtils;
    32
    33  import aQute.lib.osgi.Analyzer;
    34  import aQute.libg.header.OSGiHeader;
    35
    36
    37  /**
    38   * Add BND directives to embed selected dependencies inside a bundle
    39   *
    40   * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
    41   */
    42  public final class DependencyEmbedder extends AbstractDependencyFilter
    43  {
    44      public static final String EMBED_DEPENDENCY = "Embed-Dependency";
    45      public static final String EMBED_DIRECTORY = "Embed-Directory";
    46      public static final String EMBED_STRIP_GROUP = "Embed-StripGroup";
    47      public static final String EMBED_STRIP_VERSION = "Embed-StripVersion";
    48      public static final String EMBED_TRANSITIVE = "Embed-Transitive";
    49
    50      private static final String MAVEN_DEPENDENCIES = "{maven-dependencies}";
    51
    52      private String m_embedDirectory;
    53      private String m_embedStripGroup;
    54      private String m_embedStripVersion;
    55
    56      /**
    57       * Maven logger.
    58       */
    59      private final Log m_log;
    60
    61      /**
    62       * Inlined paths.
    63       */
    64      private final Collection m_inlinedPaths;
    65
    66      /**
    67       * Embedded artifacts.
    68       */
    69      private final Collection m_embeddedArtifacts;
    70
    71
    72      public DependencyEmbedder( Log log, Collection dependencyArtifacts )
    73      {
    74          super( dependencyArtifacts );
    75
    76          m_log = log;
    77
    78          m_inlinedPaths = new LinkedHashSet();
    79          m_embeddedArtifacts = new LinkedHashSet();
    80      }
    81
    82
    83      public void processHeaders( Analyzer analyzer ) throws MojoExecutionException
    84      {
    85          StringBuffer includeResource = new StringBuffer();
    86          StringBuffer bundleClassPath = new StringBuffer();
    87
    88          m_inlinedPaths.clear();
    89          m_embeddedArtifacts.clear();
    90
    91          String embedDependencyHeader = analyzer.getProperty( EMBED_DEPENDENCY );
    92          if ( null != embedDependencyHeader && embedDependencyHeader.length() > 0 )
    93          {
    94              m_embedDirectory = analyzer.getProperty( EMBED_DIRECTORY );
    95              m_embedStripGroup = analyzer.getProperty( EMBED_STRIP_GROUP, "true" );
    96              m_embedStripVersion = analyzer.getProperty( EMBED_STRIP_VERSION );
    97
    98              Map embedInstructions = OSGiHeader.parseHeader( embedDependencyHeader );
    99              processInstructions( embedInstructions );
    100
    101             for ( Iterator i = m_inlinedPaths.iterator(); i.hasNext(); )
    102             {
    103                 inlineDependency( ( String ) i.next(), includeResource );
    104             }
    105             for ( Iterator i = m_embeddedArtifacts.iterator(); i.hasNext(); )
    106             {
    107                 embedDependency( ( Artifact ) i.next(), includeResource, bundleClassPath );
    108             }
    109         }
    110
    111         if ( analyzer.getProperty( Analyzer.WAB ) == null && bundleClassPath.length() > 0 )
    112         {
    113             // set explicit default before merging dependency classpath
    114             if ( analyzer.getProperty( Analyzer.BUNDLE_CLASSPATH ) == null )
    115             {
    116                 analyzer.setProperty( Analyzer.BUNDLE_CLASSPATH, "." );
    117             }
    118         }
    119
    120         appendDependencies( analyzer, Analyzer.INCLUDE_RESOURCE, includeResource.toString() );
    121         appendDependencies( analyzer, Analyzer.BUNDLE_CLASSPATH, bundleClassPath.toString() );
    122     }
    123
    124
    125     @Override
    126     protected void processDependencies( String tag, String inline, Collection dependencies )
    127     {
    128         if ( dependencies.isEmpty() )
    129         {
    130             m_log.warn( EMBED_DEPENDENCY + ": clause \"" + tag + "\" did not match any dependencies" );
    131         }
    132
    133         if ( null == inline || "false".equalsIgnoreCase( inline ) )
    134         {
    135             m_embeddedArtifacts.addAll( dependencies );
    136         }
    137         else
    138         {
    139             for ( Iterator i = dependencies.iterator(); i.hasNext(); )
    140             {
    141                 addInlinedPaths( ( Artifact ) i.next(), inline, m_inlinedPaths );
    142             }
    143         }
    144     }
    145
    146
    147     private static void addInlinedPaths( Artifact dependency, String inline, Collection inlinedPaths )
    148     {
    149         File path = dependency.getFile();
    150         if ( null != path && path.exists() )
    151         {
    152             if ( "true".equalsIgnoreCase( inline ) || inline.length() == 0 )
    153             {
    154                 inlinedPaths.add( path.getPath() );
    155             }
    156             else
    157             {
    158                 String[] filters = inline.split( "\\|" );
    159                 for ( int i = 0; i < filters.length; i++ )
    160                 {
    161                     if ( filters[i].length() > 0 )
    162                     {
    163                         inlinedPaths.add( path + "!/" + filters[i] );
    164                     }
    165                 }
    166             }
    167         }
    168     }
    169
    170
    171     private void embedDependency( Artifact dependency, StringBuffer includeResource, StringBuffer bundleClassPath )
    172     {
    173         File sourceFile = dependency.getFile();
    174         if ( null != sourceFile && sourceFile.exists() )
    175         {
    176             String embedDirectory = m_embedDirectory;
    177             if ( "".equals( embedDirectory ) || ".".equals( embedDirectory ) )
    178             {
    179                 embedDirectory = null;
    180             }
    181
    182             if ( false == Boolean.valueOf( m_embedStripGroup ).booleanValue() )
    183             {
    184                 embedDirectory = new File( embedDirectory, dependency.getGroupId() ).getPath();
    185             }
    186
    187             File targetFile;
    188             if ( Boolean.valueOf( m_embedStripVersion ).booleanValue() )
    189             {
    190                 String extension = dependency.getArtifactHandler().getExtension();
    191                 if ( extension != null )
    192                 {
    193                     targetFile = new File( embedDirectory, dependency.getArtifactId() + "." + extension );
    194                 }
    195                 else
    196                 {
    197                     targetFile = new File( embedDirectory, dependency.getArtifactId() );
    198                 }
    199             }
    200             else
    201             {
    202                 targetFile = new File( embedDirectory, sourceFile.getName() );
    203             }
    204
    205             String targetFilePath = targetFile.getPath();
    206
    207             // replace windows backslash with a slash
    208             if ( File.separatorChar != '/' )
    209             {
    210                 targetFilePath = targetFilePath.replace( File.separatorChar, '/' );
    211             }
    212
    213             if ( includeResource.length() > 0 )
    214             {
    215                 includeResource.append( ',' );
    216             }
    217
    218             includeResource.append( targetFilePath );
    219             includeResource.append( '=' );
    220             includeResource.append( sourceFile );
    221
    222             if ( bundleClassPath.length() > 0 )
    223             {
    224                 bundleClassPath.append( ',' );
    225             }
    226
    227             bundleClassPath.append( targetFilePath );
    228         }
    229     }
    230
    231
    232     private static void inlineDependency( String path, StringBuffer includeResource )
    233     {
    234         if ( includeResource.length() > 0 )
    235         {
    236             includeResource.append( ',' );
    237         }
    238
    239         includeResource.append( '@' );
    240         includeResource.append( path );
    241     }
    242
    243
    244     public Collection getInlinedPaths()
    245     {
    246         return m_inlinedPaths;
    247     }
    248
    249
    250     public Collection getEmbeddedArtifacts()
    251     {
    252         return m_embeddedArtifacts;
    253     }
    254
    255
    256     private static void appendDependencies( Analyzer analyzer, String directiveName, String mavenDependencies )
    257     {
    258         /*
    259          * similar algorithm to {maven-resources} but default behaviour here is to append rather than override
    260          */
    261         final String instruction = analyzer.getProperty( directiveName );
    262         if ( instruction != null && instruction.length() > 0 )
    263         {
    264             if ( instruction.indexOf( MAVEN_DEPENDENCIES ) >= 0 )
    265             {
    266                 // if there are no embeddded dependencies, we do a special treatment and replace
    267                 // every occurance of MAVEN_DEPENDENCIES and a following comma with an empty string
    268                 if ( mavenDependencies.length() == 0 )
    269                 {
    270                     String cleanInstruction = BundlePlugin.removeTagFromInstruction( instruction, MAVEN_DEPENDENCIES );
    271                     analyzer.setProperty( directiveName, cleanInstruction );
    272                 }
    273                 else
    274                 {
    275                     String mergedInstruction = StringUtils.replace( instruction, MAVEN_DEPENDENCIES, mavenDependencies );
    276                     analyzer.setProperty( directiveName, mergedInstruction );
    277                 }
    278             }
    279             else if ( mavenDependencies.length() > 0 )
    280             {
    281                 if ( Analyzer.INCLUDE_RESOURCE.equalsIgnoreCase( directiveName ) )
    282                 {
    283                     // dependencies should be prepended so they can be overwritten by local resources
    284                     analyzer.setProperty( directiveName, mavenDependencies + ',' + instruction );
    285                 }
    286                 else
    287                 // Analyzer.BUNDLE_CLASSPATH
    288                 {
    289                     // for the classpath we want dependencies to be appended after local entries
    290                     analyzer.setProperty( directiveName, instruction + ',' + mavenDependencies );
    291                 }
    292             }
    293             // otherwise leave instruction unchanged
    294         }
    295         else if ( mavenDependencies.length() > 0 )
    296         {
    297             analyzer.setProperty( directiveName, mavenDependencies );
    298         }
    299         // otherwise leave instruction unchanged
    300     }
    301 }