Return-Path: Delivered-To: apmail-geronimo-scm-archive@www.apache.org Received: (qmail 60696 invoked from network); 13 Jan 2010 21:23:50 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 13 Jan 2010 21:23:50 -0000 Received: (qmail 47772 invoked by uid 500); 13 Jan 2010 21:23:50 -0000 Delivered-To: apmail-geronimo-scm-archive@geronimo.apache.org Received: (qmail 47700 invoked by uid 500); 13 Jan 2010 21:23:50 -0000 Mailing-List: contact scm-help@geronimo.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: dev@geronimo.apache.org List-Id: Delivered-To: mailing list scm@geronimo.apache.org Received: (qmail 47691 invoked by uid 99); 13 Jan 2010 21:23:50 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Jan 2010 21:23:50 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Jan 2010 21:23:48 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 93C1423889EB; Wed, 13 Jan 2010 21:23:28 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r898946 - in /geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi: BundleClassFinder.java BundleDescription.java Date: Wed, 13 Jan 2010 21:23:28 -0000 To: scm@geronimo.apache.org From: gawor@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100113212328.93C1423889EB@eris.apache.org> Author: gawor Date: Wed Jan 13 21:23:28 2010 New Revision: 898946 URL: http://svn.apache.org/viewvc?rev=898946&view=rev Log: GERONIMO-5026: First stab at bundle class finder Added: geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleClassFinder.java (with props) Modified: geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleDescription.java Added: geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleClassFinder.java URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleClassFinder.java?rev=898946&view=auto ============================================================================== --- geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleClassFinder.java (added) +++ geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleClassFinder.java Wed Jan 13 21:23:28 2010 @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geronimo.kernel.osgi; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.geronimo.kernel.osgi.BundleDescription.ExportPackage; +import org.apache.geronimo.kernel.osgi.BundleDescription.HeaderEntry; +import org.apache.geronimo.kernel.osgi.BundleDescription.RequireBundle; +import org.osgi.framework.Bundle; +import org.osgi.service.packageadmin.ExportedPackage; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.service.packageadmin.RequiredBundle; + +/** + * Finds all available classes to a bundle by scanning Bundle-ClassPath, + * Import-Package, and Require-Bundle headers of the given bundle and its fragments. + * DynamicImport-Package header is not considered during scanning. + * + * @version $Rev$ $Date$ + */ +public class BundleClassFinder { + + private static final String EXT = ".class"; + private static final String PATTERN = "*.class"; + + private Bundle bundle; + private boolean scanImportPackages; + private PackageAdmin packageAdmin; + private Map> classMap; + + public BundleClassFinder(PackageAdmin packageAdmin, Bundle bundle) { + this.packageAdmin = packageAdmin; + this.bundle = bundle; + this.scanImportPackages = true; + } + + public void setScanImportPackages(boolean searchImports) { + this.scanImportPackages = searchImports; + } + + public boolean getScanImportPackages() { + return this.scanImportPackages; + } + + public List loadClasses(Set classes) { + List loadedClasses = new ArrayList(); + for (String clazz : classes) { + try { + loadedClasses.add(bundle.loadClass(clazz)); + } catch (Exception ignore) { + // ignore + } + } + return loadedClasses; + } + + /** + * Finds all available classes to the bundle. Some of the classes in the returned set + * might not be loadable. + * + * @return classes visible to the bundle. Not all classes returned might be loadable. + */ + public Set findAll() { + Set classes = new LinkedHashSet(); + + classMap = new HashMap>(); + + scanImportPackages(classes, bundle, bundle); + scanRequireBundles(classes, bundle); + scanBundleClassPath(classes, bundle); + + Bundle[] fragments = packageAdmin.getFragments(bundle); + if (fragments != null) { + for (Bundle fragment : fragments) { + scanImportPackages(classes, bundle, fragment); + scanRequireBundles(classes, fragment); + scanBundleClassPath(classes, fragment); + } + } + + classMap.clear(); + + return classes; + } + + private void scanImportPackages(Collection classes, Bundle host, Bundle fragment) { + if (!scanImportPackages) { + return; + } + BundleDescription description = new BundleDescription(fragment.getHeaders()); + + List imports = description.getExternalImports(); + HashSet wiredBundles = new HashSet(); + for (BundleDescription.ImportPackage packageImport : imports) { + ExportedPackage[] exports = packageAdmin.getExportedPackages(packageImport.getName()); + Bundle wiredBundle = isWired(host, exports); + if (wiredBundle != null) { + wiredBundles.add(wiredBundle); + Set allClasses = findAllClasses(wiredBundle); + addMatchingClasses(classes, allClasses, packageImport.getName()); + } + } + } + + private Set findAllClasses(Bundle bundle) { + Set allClasses = classMap.get(bundle); + if (allClasses == null) { + BundleClassFinder finder = new BundleClassFinder(packageAdmin, bundle); + finder.setScanImportPackages(false); + allClasses = finder.findAll(); + classMap.put(bundle, allClasses); + } + return allClasses; + } + + private void addMatchingClasses(Collection classes, Set allClasses, String packageName) { + String prefix = packageName + "."; + for (String clazz : allClasses) { + if (clazz.startsWith(prefix) && clazz.indexOf('.', prefix.length()) == -1) { + classes.add(clazz); + } + } + } + + private void scanRequireBundles(Collection classes, Bundle bundle) { + BundleDescription description = new BundleDescription(bundle.getHeaders()); + List requiredBundleList = description.getRequireBundle(); + for (RequireBundle requiredBundle : requiredBundleList) { + RequiredBundle[] requiredBundles = packageAdmin.getRequiredBundles(requiredBundle.getName()); + Bundle wiredBundle = isWired(bundle, requiredBundles); + if (wiredBundle != null) { + Set allClasses = findAllClasses(wiredBundle); + BundleDescription wiredBundleDescription = new BundleDescription(wiredBundle.getHeaders()); + List exportPackages = wiredBundleDescription.getExportPackage(); + for (ExportPackage exportPackage : exportPackages) { + addMatchingClasses(classes, allClasses, exportPackage.getName()); + } + } + } + } + + private void scanBundleClassPath(Collection resources, Bundle bundle) { + BundleDescription description = new BundleDescription(bundle.getHeaders()); + List paths = description.getBundleClassPath(); + if (paths.isEmpty()) { + scanDirectory(resources, bundle, "/"); + } else { + for (HeaderEntry path : paths) { + String name = path.getName(); + if (name.equals(".") || name.equals("/")) { + // scan root + scanDirectory(resources, bundle, "/"); + } else if (name.endsWith(".jar") || name.endsWith(".zip")) { + // scan embedded jar/zip + scanZip(resources, bundle, name); + } else { + // assume it's a directory + scanDirectory(resources, bundle, "/" + name); + } + } + } + } + + private void scanDirectory(Collection classes, Bundle bundle, String basePath) { + if (!basePath.endsWith("/")) { + basePath = basePath + "/"; + } + Enumeration e = bundle.findEntries(basePath, PATTERN, true); + if (e != null) { + while (e.hasMoreElements()) { + URL u = (URL) e.nextElement(); + String name = u.getPath().substring(basePath.length()); + classes.add(toClassName(name)); + } + } + } + + private void scanZip(Collection classes, Bundle bundle, String zipName) { + URL zipEntry = bundle.getEntry(zipName); + if (zipEntry == null) { + return; + } + ZipInputStream in = null; + try { + in = new ZipInputStream(zipEntry.openStream()); + ZipEntry entry; + while ((entry = in.getNextEntry()) != null) { + String name = entry.getName(); + if (name.endsWith(EXT)) { + classes.add(toClassName(name)); + } + } + } catch (IOException ignore) { + // ignore + } finally { + if (in != null) { + try { in.close(); } catch (IOException e) {} + } + } + } + + private static String toClassName(String name) { + name = name.substring(0, name.length() - EXT.length()); + name = name.replaceAll("/", "."); + return name; + } + + private static Bundle isWired(Bundle bundle, ExportedPackage[] exports) { + if (exports != null) { + for (ExportedPackage exportedPackage : exports) { + Bundle[] importingBundles = exportedPackage.getImportingBundles(); + if (importingBundles != null) { + for (Bundle importingBundle : importingBundles) { + if (importingBundle == bundle) { + return exportedPackage.getExportingBundle(); + } + } + } + } + } + return null; + } + + private static Bundle isWired(Bundle bundle, RequiredBundle[] requiredBundles) { + if (requiredBundles != null) { + for (RequiredBundle requiredBundle : requiredBundles) { + Bundle[] requiringBundles = requiredBundle.getRequiringBundles(); + if (requiringBundles != null) { + for (Bundle requiringBundle : requiringBundles) { + if (requiringBundle == bundle) { + return requiredBundle.getBundle(); + } + } + } + } + } + return null; + } + +} Propchange: geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleClassFinder.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleClassFinder.java ------------------------------------------------------------------------------ svn:keywords = Date Revision Propchange: geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleClassFinder.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleDescription.java URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleDescription.java?rev=898946&r1=898945&r2=898946&view=diff ============================================================================== --- geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleDescription.java (original) +++ geronimo/server/trunk/framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/osgi/BundleDescription.java Wed Jan 13 21:23:28 2010 @@ -137,6 +137,14 @@ return parseStandardHeader(headerValue); } + /** + * Returns a list of paths that are listed in Bundle-ClassPath header. + */ + public List getBundleClassPath() { + String headerValue = (String) headers.get(Constants.BUNDLE_CLASSPATH); + return parseStandardHeader(headerValue); + } + public SymbolicName getSymbolicName() { String headerValue = (String) headers.get(Constants.BUNDLE_SYMBOLICNAME); List elements = HeaderParser.parseHeader(headerValue);