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 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       * @return All the exported packages of all bundles on the classpath.
53       */
54      public static Set<String> getAllBundleExports( FileFilter filter, Set<String> pkgs )
55      {
56          if ( pkgs == null )
57          {
58              pkgs = new HashSet<String>();
59          }
60  
61          Set<File> candidates = getClasspathCandidates( filter );
62  
63          for ( File candidate : candidates )
64          {
65              String exports = getBundleExports( candidate );
66  
67              if ( exports == null )
68              {
69                  LOG.debug( "No export found for candidate: {}", candidate );
70                  continue;
71              }
72  
73              LOG.debug( "Processing exports for candidate: {}\n\n{}\n", candidate, exports );
74              splitIntoPackages( exports, pkgs );
75          }
76  
77          return pkgs;
78      }
79  
80  
81      /**
82       * Splits an Package-Export OSGi Manifest Attribute value into packages
83       * while stripping away the key/value properties.
84       *
85       * @param exports The Package-Export OSGi Manifest Attribute value.
86       * @return The set of exported packages without properties.
87       */
88      public static Set<String> splitIntoPackages( String exports, Set<String> pkgs )
89      {
90          if ( pkgs == null )
91          {
92              pkgs = new HashSet<String>();
93          }
94  
95          int index = 0;
96          boolean inPkg = true;
97          boolean inProps = false;
98          StringBuilder pkg = new StringBuilder();
99  
100         while ( index < exports.length() )
101         {
102             if ( inPkg && exports.charAt( index ) != ';' )
103             {
104                 pkg.append( exports.charAt( index ) );
105                 index++;
106             }
107             else if ( inPkg && exports.charAt( index ) == ';' )
108             {
109                 inPkg = false;
110                 inProps = true;
111 
112                 pkgs.add( pkg.toString() );
113                 LOG.debug( "Added package: {}", pkg.toString() );
114                 pkg.setLength( 0 );
115 
116                 index += 8;
117             }
118             else if ( inProps && exports.charAt( index ) == '"'
119                 && index + 1 < exports.length()
120                 && exports.charAt( index + 1 ) == ',' )
121             {
122                 inPkg = true;
123                 inProps = false;
124                 index += 2;
125             }
126             else if ( inProps )
127             {
128                 index++;
129             }
130             else
131             {
132                 LOG.error( "Unexpected parser condition throwing IllegalStateException." );
133                 throw new IllegalStateException( "Should never get here!" );
134             }
135         }
136 
137         return pkgs;
138     }
139 
140 
141     public static Set<File> getClasspathCandidates( FileFilter filter )
142     {
143         Set<File> candidates = new HashSet<File>();
144         String separator = System.getProperty( "path.separator" );
145         String[] cpElements = System.getProperty( "java.class.path" ).split( separator );
146 
147         for ( String element : cpElements )
148         {
149             File candidate = new File( element );
150 
151             if ( candidate.isFile() )
152             {
153                 if ( filter != null && filter.accept( candidate ) )
154                 {
155                     candidates.add( candidate );
156                     LOG.info( "Accepted candidate with filter: {}", candidate.toString() );
157                 }
158                 else if ( filter == null && candidate.getName().endsWith( ".jar" ) )
159                 {
160                     candidates.add( candidate );
161                     LOG.info( "Accepted candidate without filter: {}", candidate.toString() );
162                 }
163                 else
164                 {
165                     LOG.info( "Rejecting candidate: {}", candidate.toString() );
166                 }
167             }
168         }
169 
170         return candidates;
171     }
172 
173 
174     /**
175      * Gets the attribute value for the Export-Bundle OSGi Manifest Attribute.
176      * 
177      * @param bundle The absolute path to a file bundle.
178      * @return The value as it appears in the Manifest, as a comma delimited
179      * list of packages with possible "uses" phrases appended to each package
180      * or null if the attribute does not exist.
181      */
182     public static String getBundleExports( File bundle )
183     {
184         JarFile jar = null;
185         try
186         {
187             jar = new JarFile( bundle );
188             Manifest manifest = jar.getManifest();
189 
190             if ( manifest == null )
191             {
192                 return null;
193             }
194 
195             for ( Map.Entry<Object, Object> attr : manifest.getMainAttributes().entrySet() )
196             {
197                 if ( attr.getKey().toString().equals( "Export-Package" ) )
198                 {
199                     return attr.getValue().toString();
200                 }
201             }
202 
203             return null;
204         }
205         catch ( IOException e )
206         {
207             LOG.error( "Failed to open jar file or manifest.", e );
208             throw new RuntimeException( "Failed to open jar file or manifest.", e );
209         }
210         finally
211         {
212             if ( jar != null )
213             {
214                 try
215                 {
216                     jar.close();
217                 }
218                 catch ( IOException e )
219                 {
220                     e.printStackTrace();
221                 }
222             }
223         }
224     }
225 }