View Javadoc
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   */
20  package org.apache.directory.api.util;
21  
22  
23  import java.io.File;
24  import java.io.FileFilter;
25  import java.io.IOException;
26  import java.util.HashSet;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.jar.JarFile;
30  import java.util.jar.Manifest;
31  
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  
36  /**
37   * Utilities for OSGi environments and embedding OSGi containers.
38   *
39   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
40   */
41  public final class OsgiUtils
42  {
43      /** A logger */
44      private static final Logger LOG = LoggerFactory.getLogger( OsgiUtils.class );
45  
46  
47      /**
48       * All the packages that are exported from all bundles found on the system
49       * classpath. The provided filter if not null is used to prune classpath
50       * elements. Any uses terms found are stripped from the bundles.
51       *
52       * @param filter The filter to use on the files
53       * @param pkgs The set of packages to use
54       * @return All the exported packages of all bundles on the classpath.
55       */
56      public static Set<String> getAllBundleExports( FileFilter filter, Set<String> pkgs )
57      {
58          if ( pkgs == null )
59          {
60              pkgs = new HashSet<String>();
61          }
62  
63          Set<File> candidates = getClasspathCandidates( filter );
64  
65          for ( File candidate : candidates )
66          {
67              String exports = getBundleExports( candidate );
68  
69              if ( exports == null )
70              {
71                  LOG.debug( "No export found for candidate: {}", candidate );
72                  continue;
73              }
74  
75              LOG.debug( "Processing exports for candidate: {}\n\n{}\n", candidate, exports );
76              splitIntoPackages( exports, pkgs );
77          }
78  
79          return pkgs;
80      }
81  
82  
83      /**
84       * Splits a Package-Export OSGi Manifest Attribute value into packages
85       * while stripping away the key/value properties.
86       *
87       * @param exports The Package-Export OSGi Manifest Attribute value.
88       * @param pkgs The set that will contain the found packages.
89       * @return The set of exported packages without properties.
90       */
91      public static Set<String> splitIntoPackages( String exports, Set<String> pkgs )
92      {
93          if ( pkgs == null )
94          {
95              pkgs = new HashSet<String>();
96          }
97  
98          int index = 0;
99          boolean inPkg = true;
100         boolean inProps = false;
101         StringBuilder pkg = new StringBuilder();
102 
103         while ( index < exports.length() )
104         {
105             if ( inPkg && exports.charAt( index ) != ';' )
106             {
107                 pkg.append( exports.charAt( index ) );
108                 index++;
109             }
110             else if ( inPkg && exports.charAt( index ) == ';' )
111             {
112                 inPkg = false;
113                 inProps = true;
114 
115                 pkgs.add( pkg.toString() );
116                 LOG.debug( "Added package: {}", pkg.toString() );
117                 pkg.setLength( 0 );
118 
119                 index += 8;
120             }
121             else if ( inProps && exports.charAt( index ) == '"'
122                 && index + 1 < exports.length()
123                 && exports.charAt( index + 1 ) == ',' )
124             {
125                 inPkg = true;
126                 inProps = false;
127                 index += 2;
128             }
129             else if ( inProps )
130             {
131                 index++;
132             }
133             else
134             {
135                 LOG.error( "Unexpected parser condition throwing IllegalStateException." );
136                 throw new IllegalStateException( "Should never get here!" );
137             }
138         }
139 
140         return pkgs;
141     }
142 
143 
144     /**
145      * Get the files that fits a given filter
146      *
147      * @param filter The filter in use
148      * @return The set of Files that match the filter
149      */
150     public static Set<File> getClasspathCandidates( FileFilter filter )
151     {
152         Set<File> candidates = new HashSet<File>();
153         String separator = System.getProperty( "path.separator" );
154         String[] cpElements = System.getProperty( "java.class.path" ).split( separator );
155 
156         for ( String element : cpElements )
157         {
158             File candidate = new File( element );
159 
160             if ( candidate.isFile() )
161             {
162                 if ( filter != null && filter.accept( candidate ) )
163                 {
164                     candidates.add( candidate );
165                     LOG.info( "Accepted candidate with filter: {}", candidate.toString() );
166                 }
167                 else if ( filter == null && candidate.getName().endsWith( ".jar" ) )
168                 {
169                     candidates.add( candidate );
170                     LOG.info( "Accepted candidate without filter: {}", candidate.toString() );
171                 }
172                 else
173                 {
174                     LOG.info( "Rejecting candidate: {}", candidate.toString() );
175                 }
176             }
177         }
178 
179         return candidates;
180     }
181 
182 
183     /**
184      * Gets the attribute value for the Export-Bundle OSGi Manifest Attribute.
185      * 
186      * @param bundle The absolute path to a file bundle.
187      * @return The value as it appears in the Manifest, as a comma delimited
188      * list of packages with possible "uses" phrases appended to each package
189      * or null if the attribute does not exist.
190      */
191     public static String getBundleExports( File bundle )
192     {
193         try ( JarFile jar = new JarFile( bundle ) )
194         {
195             Manifest manifest = jar.getManifest();
196 
197             if ( manifest == null )
198             {
199                 return null;
200             }
201 
202             for ( Map.Entry<Object, Object> attr : manifest.getMainAttributes().entrySet() )
203             {
204                 if ( attr.getKey().toString().equals( "Export-Package" ) )
205                 {
206                     return attr.getValue().toString();
207                 }
208             }
209 
210             return null;
211         }
212         catch ( IOException e )
213         {
214             LOG.error( "Failed to open jar file or manifest.", e );
215             throw new RuntimeException( "Failed to open jar file or manifest.", e );
216         }
217     }
218 
219 
220     private OsgiUtils()
221     {
222     }
223 }