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.ArrayList;
025 import java.util.Collections;
026 import java.util.Enumeration;
027 import java.util.Iterator;
028 import java.util.LinkedHashSet;
029 import java.util.List;
030
031 import org.osgi.framework.Bundle;
032 import org.osgi.framework.BundleReference;
033
034 /**
035 * ClassLoader for a {@link Bundle}.
036 * <br/>
037 * In OSGi, resource lookup on resources in the <i>META-INF</i> directory using {@link Bundle#getResource(String)} or
038 * {@link Bundle#getResources(String)} does not return the resources found in the wired bundles of the bundle
039 * (wired via <i>Import-Package</i> or <i>DynamicImport-Package</i>). This class loader implementation provides
040 * {@link #getResource(String) and {@link #getResources(String)} methods that do delegate such resource lookups to
041 * the wired bundles.
042 *
043 * @version $Rev: 938291 $ $Date: 2010-04-27 03:53:06 +0200 (Tue, 27 Apr 2010) $
044 */
045 public class BundleClassLoader extends ClassLoader implements BundleReference {
046
047 private final static String META_INF_1 = "META-INF/";
048 private final static String META_INF_2 = "/META-INF/";
049
050 private final Bundle bundle;
051 private boolean searchWiredBundles;
052
053 public BundleClassLoader(Bundle bundle) {
054 this(bundle, true);
055 }
056
057 public BundleClassLoader(Bundle bundle, boolean searchWiredBundles) {
058 this.bundle = bundle;
059 this.searchWiredBundles = searchWiredBundles;
060 }
061
062 @Override
063 public Class<?> loadClass(String name) throws ClassNotFoundException {
064 return loadClass(name, false);
065 }
066
067 @Override
068 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
069 Class clazz = bundle.loadClass(name);
070 if (resolve) {
071 resolveClass(clazz);
072 }
073 return clazz;
074 }
075
076 @Override
077 public String toString() {
078 return "[BundleClassLoader] " + bundle;
079 }
080
081 @Override
082 public URL getResource(String name) {
083 URL resource = bundle.getResource(name);
084 if (resource == null && isMetaInfResource(name)) {
085 LinkedHashSet<Bundle> wiredBundles = BundleUtils.getWiredBundles(bundle);
086 Iterator<Bundle> iterator = wiredBundles.iterator();
087 while (iterator.hasNext() && resource == null) {
088 resource = iterator.next().getResource(name);
089 }
090 }
091 return resource;
092 }
093
094 @SuppressWarnings("unchecked")
095 @Override
096 public Enumeration<URL> getResources(String name) throws IOException {
097 Enumeration<URL> e = (Enumeration<URL>) bundle.getResources(name);
098 if (isMetaInfResource(name)) {
099 ArrayList<URL> allResources = new ArrayList<URL>();
100 addToList(allResources, e);
101 LinkedHashSet<Bundle> wiredBundles = BundleUtils.getWiredBundles(bundle);
102 for (Bundle wiredBundle : wiredBundles) {
103 Enumeration<URL> resources = wiredBundle.getResources(name);
104 addToList(allResources, resources);
105 }
106 return Collections.enumeration(allResources);
107 } else {
108 if (e == null) {
109 return Collections.enumeration(Collections.EMPTY_LIST);
110 } else {
111 return e;
112 }
113 }
114 }
115
116 public void setSearchWiredBundles(boolean search) {
117 searchWiredBundles = search;
118 }
119
120 public boolean getSearchWiredBundles() {
121 return searchWiredBundles;
122 }
123
124 private boolean isMetaInfResource(String name) {
125 return searchWiredBundles && name != null && (name.startsWith(META_INF_1) || name.startsWith(META_INF_2));
126 }
127
128 private void addToList(List<URL> list, Enumeration<URL> enumeration) {
129 if (enumeration != null) {
130 while (enumeration.hasMoreElements()) {
131 list.add(enumeration.nextElement());
132 }
133 }
134 }
135
136 /**
137 * Return the bundle associated with this classloader.
138 *
139 * In most cases the bundle associated with the classloader is a regular framework bundle.
140 * However, in some cases the bundle associated with the classloader is a {@link DelegatingBundle}.
141 * In such cases, the <tt>unwrap</tt> parameter controls whether this function returns the
142 * {@link DelegatingBundle} instance or the main application bundle backing with the {@link DelegatingBundle}.
143 *
144 * @param unwrap If true and if the bundle associated with this classloader is a {@link DelegatingBundle},
145 * this function will return the main application bundle backing with the {@link DelegatingBundle}.
146 * Otherwise, the bundle associated with this classloader is returned as is.
147 * @return The bundle associated with this classloader.
148 */
149 public Bundle getBundle(boolean unwrap) {
150 if (unwrap && bundle instanceof DelegatingBundle) {
151 return ((DelegatingBundle) bundle).getMainBundle();
152 }
153 return bundle;
154 }
155
156 /**
157 * Return the bundle associated with this classloader.
158 *
159 * This method calls {@link #getBundle(boolean) getBundle(true)} and therefore always returns a regular
160 * framework bundle.
161 * <br><br>
162 * Note: Some libraries use {@link BundleReference#getBundle()} to obtain a bundle for the given
163 * classloader and expect the returned bundle instance to be work with any OSGi API. Some of these API might
164 * not work if {@link DelegatingBundle} is returned. That is why this function will always return
165 * a regular framework bundle. See {@link #getBundle(boolean)} for more information.
166 *
167 * @return The bundle associated with this classloader.
168 */
169 public Bundle getBundle() {
170 return getBundle(true);
171 }
172
173 @Override
174 public int hashCode() {
175 return bundle.hashCode();
176 }
177
178 @Override
179 public boolean equals(Object other) {
180 if (other == this) {
181 return true;
182 }
183 if (other == null || !other.getClass().equals(getClass())) {
184 return false;
185 }
186 BundleClassLoader otherBundleClassLoader = (BundleClassLoader) other;
187 return this.bundle == otherBundleClassLoader.bundle;
188 }
189
190 }