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}