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 }