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.api.util; 021 022 023import java.io.File; 024import java.io.FileFilter; 025import java.io.IOException; 026import java.util.HashSet; 027import java.util.Map; 028import java.util.Set; 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 final 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 * @param filter The filter to use on the files 053 * @param pkgs The set of packages to use 054 * @return All the exported packages of all bundles on the classpath. 055 */ 056 public static Set<String> getAllBundleExports( FileFilter filter, Set<String> pkgs ) 057 { 058 if ( pkgs == null ) 059 { 060 pkgs = new HashSet<String>(); 061 } 062 063 Set<File> candidates = getClasspathCandidates( filter ); 064 065 for ( File candidate : candidates ) 066 { 067 String exports = getBundleExports( candidate ); 068 069 if ( exports == null ) 070 { 071 LOG.debug( "No export found for candidate: {}", candidate ); 072 continue; 073 } 074 075 LOG.debug( "Processing exports for candidate: {}\n\n{}\n", candidate, exports ); 076 splitIntoPackages( exports, pkgs ); 077 } 078 079 return pkgs; 080 } 081 082 083 /** 084 * Splits a Package-Export OSGi Manifest Attribute value into packages 085 * while stripping away the key/value properties. 086 * 087 * @param exports The Package-Export OSGi Manifest Attribute value. 088 * @param pkgs The set that will contain the found packages. 089 * @return The set of exported packages without properties. 090 */ 091 public static Set<String> splitIntoPackages( String exports, Set<String> pkgs ) 092 { 093 if ( pkgs == null ) 094 { 095 pkgs = new HashSet<String>(); 096 } 097 098 int index = 0; 099 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}