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.xbean.osgi.bundle.util; 021 022import java.io.IOException; 023import java.net.URL; 024import java.util.Enumeration; 025 026import org.osgi.framework.Bundle; 027import org.osgi.framework.BundleReference; 028 029/** 030 * ClassLoader for a {@link Bundle}. 031 * <br/> 032 * In OSGi, resource lookup on resources in the <i>META-INF</i> directory using {@link Bundle#getResource(String)} or 033 * {@link Bundle#getResources(String)} does not return the resources found in the wired bundles of the bundle 034 * (wired via <i>Import-Package</i> or <i>DynamicImport-Package</i>). This class loader implementation provides 035 * {@link #getResource(String) and {@link #getResources(String)} methods that do delegate such resource lookups to 036 * the wired bundles. 037 * <br/> 038 * The URLs returned by {@link Bundle#getResource(String)} or {@link Bundle#getResources(String)} methods are 039 * OSGi framework specific "bundle" URLs. This sometimes can cause problems with 3rd party libraries 040 * which do not understand how to interpret the "bundle" URLs. This ClassLoader implementation, if enabled, 041 * can return <tt>jar</tt> URLs for resources found in embedded jars in the bundle. If a resource is found within a 042 * directory in the bundle the URL returned for that resource is unconverted. 043 * 044 * @version $Rev: 1163514 $ $Date: 2011-08-31 09:37:38 +0200 (Wed, 31 Aug 2011) $ 045 */ 046public class BundleClassLoader extends ClassLoader implements DelegatingBundleReference { 047 048 protected final Bundle bundle; 049 protected final BundleResourceHelper resourceHelper; 050 051 public BundleClassLoader(Bundle bundle) { 052 this(bundle, 053 BundleResourceHelper.getSearchWiredBundles(true), 054 BundleResourceHelper.getConvertResourceUrls(false)); 055 } 056 057 public BundleClassLoader(Bundle bundle, boolean searchWiredBundles) { 058 this(bundle, 059 searchWiredBundles, 060 BundleResourceHelper.getConvertResourceUrls(false)); 061 } 062 063 public BundleClassLoader(Bundle bundle, boolean searchWiredBundles, boolean convertResourceUrls) { 064 this.bundle = bundle; 065 this.resourceHelper = new BundleResourceHelper(bundle, searchWiredBundles, convertResourceUrls); 066 } 067 068 @Override 069 public String toString() { 070 return "[BundleClassLoader] " + bundle; 071 } 072 073 @Override 074 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 075 Class clazz = bundle.loadClass(name); 076 if (resolve) { 077 resolveClass(clazz); 078 } 079 return clazz; 080 } 081 082 @Override 083 public URL getResource(String name) { 084 return resourceHelper.getResource(name); 085 } 086 087 @Override 088 public Enumeration<URL> getResources(String name) throws IOException { 089 return resourceHelper.getResources(name); 090 } 091 092 @Override 093 public Enumeration<URL> findResources (String name) throws IOException { 094 return this.getResources(name); 095 } 096 097 public void setSearchWiredBundles(boolean search) { 098 resourceHelper.setSearchWiredBundles(search); 099 } 100 101 public boolean getSearchWiredBundles() { 102 return resourceHelper.getSearchWiredBundles(); 103 } 104 105 public void setConvertResourceUrls(boolean convert) { 106 resourceHelper.setConvertResourceUrls(convert); 107 } 108 109 public boolean getConvertResourceUrls() { 110 return resourceHelper.getConvertResourceUrls(); 111 } 112 113 /** 114 * Return the bundle associated with this classloader. 115 * 116 * In most cases the bundle associated with the classloader is a regular framework bundle. 117 * However, in some cases the bundle associated with the classloader is a {@link DelegatingBundle}. 118 * In such cases, the <tt>unwrap</tt> parameter controls whether this function returns the 119 * {@link DelegatingBundle} instance or the main application bundle backing with the {@link DelegatingBundle}. 120 * 121 * @param unwrap If true and if the bundle associated with this classloader is a {@link DelegatingBundle}, 122 * this function will return the main application bundle backing with the {@link DelegatingBundle}. 123 * Otherwise, the bundle associated with this classloader is returned as is. 124 * @return The bundle associated with this classloader. 125 */ 126 public Bundle getBundle(boolean unwrap) { 127 if (unwrap && bundle instanceof DelegatingBundle) { 128 return ((DelegatingBundle) bundle).getMainBundle(); 129 } 130 return bundle; 131 } 132 133 /** 134 * Return the bundle associated with this classloader. 135 * 136 * This method calls {@link #getBundle(boolean) getBundle(true)} and therefore always returns a regular 137 * framework bundle. 138 * <br><br> 139 * Note: Some libraries use {@link BundleReference#getBundle()} to obtain a bundle for the given 140 * classloader and expect the returned bundle instance to be work with any OSGi API. Some of these API might 141 * not work if {@link DelegatingBundle} is returned. That is why this function will always return 142 * a regular framework bundle. See {@link #getBundle(boolean)} for more information. 143 * 144 * @return The bundle associated with this classloader. 145 */ 146 public Bundle getBundle() { 147 return getBundle(true); 148 } 149 150 @Override 151 public int hashCode() { 152 return bundle.hashCode(); 153 } 154 155 @Override 156 public boolean equals(Object other) { 157 if (other == this) { 158 return true; 159 } 160 if (other == null || !other.getClass().equals(getClass())) { 161 return false; 162 } 163 BundleClassLoader otherBundleClassLoader = (BundleClassLoader) other; 164 return this.bundle == otherBundleClassLoader.bundle; 165 } 166 167}