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
020 package org.apache.xbean.osgi.bundle.util;
021
022 import java.io.IOException;
023 import java.net.URL;
024 import java.util.Enumeration;
025
026 import org.osgi.framework.Bundle;
027 import 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 15:37:38 +0800 (Wed, 31 Aug 2011) $
045 */
046 public 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 }