001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *   Unless required by applicable law or agreed to in writing,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020package org.apache.directory.shared.util;
021
022import java.io.File;
023import java.io.FileFilter;
024import java.io.IOException;
025import java.util.HashSet;
026import java.util.Map;
027import java.util.Set;
028import java.util.jar.Attributes;
029import java.util.jar.JarFile;
030import java.util.jar.Manifest;
031
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035
036/**
037 * Utilities for OSGi environments and embedding OSGi containers.
038 *
039 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040 */
041public class OsgiUtils
042{
043    /** A logger */
044    private static final Logger LOG = LoggerFactory.getLogger( OsgiUtils.class );
045    
046    
047    /**
048     * All the packages that are exported from all bundles found on the system 
049     * classpath. The provided filter if not null is used to prune classpath 
050     * elements. Any uses terms found are stripped from the bundles.
051     *
052     * @return All the exported packages of all bundles on the classpath.
053     */
054    public static Set<String> getAllBundleExports( FileFilter filter, Set<String> pkgs )
055    {
056        if ( pkgs == null )
057        {
058            pkgs = new HashSet<String>();
059        }
060        
061        Set<File> candidates = getClasspathCandidates( filter );
062        
063        for ( File candidate : candidates )
064        {
065            String exports = getBundleExports( candidate );
066            
067            if ( exports == null )
068            {
069                LOG.debug( "No export found for candidate: {}", candidate );
070                continue;
071            }
072            
073            LOG.debug( "Processing exports for candidate: {}\n\n{}\n", candidate, exports );
074            splitIntoPackages( exports, pkgs );
075        }
076        
077        return pkgs;
078    }
079
080    
081    /**
082     * Splits an Package-Export OSGi Manifest Attribute value into packages 
083     * while stripping away the key/value properties.
084     *
085     * @param exports The Package-Export OSGi Manifest Attribute value.
086     * @return The set of exported packages without properties.
087     */
088    public static Set<String> splitIntoPackages( String exports, Set<String> pkgs )
089    {
090        if ( pkgs == null )
091        {
092            pkgs = new HashSet<String>();
093        }
094        
095        int index = 0;
096        boolean inPkg = true;
097        boolean inProps = false;
098        StringBuilder pkg = new StringBuilder();
099        
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;
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    }
211}