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