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.io.InputStream;
024 import java.net.URL;
025 import java.util.ArrayList;
026 import java.util.Collections;
027 import java.util.Enumeration;
028 import java.util.Iterator;
029 import java.util.LinkedHashSet;
030 import java.util.List;
031 import java.util.zip.ZipEntry;
032
033 import org.osgi.framework.Bundle;
034 import org.osgi.framework.ServiceReference;
035 import org.osgi.service.packageadmin.PackageAdmin;
036
037 /**
038 * Helper for finding resources in a {@link Bundle}.
039 * <br/>
040 * In OSGi, resource lookup on resources in the <i>META-INF</i> directory using {@link Bundle#getResource(String)} or
041 * {@link Bundle#getResources(String)} does not return the resources found in the wired bundles of the bundle
042 * (wired via <i>Import-Package</i> or <i>DynamicImport-Package</i>). This class loader implementation provides
043 * {@link #getResource(String) and {@link #getResources(String)} methods that do delegate <i>META-INF</i> resource lookups
044 * to the wired bundles.
045 * <br/>
046 * The URLs returned by {@link Bundle#getResource(String)} or {@link Bundle#getResources(String)} methods are
047 * OSGi framework specific "bundle" URLs. If enabled, this helper can convert the framework specific URLs into
048 * regular <tt>jar</tt> URLs.
049 *
050 * @version $Rev: 1095603 $ $Date: 2011-04-21 14:29:46 +0800 (Thu, 21 Apr 2011) $
051 */
052 public class BundleResourceHelper {
053
054 public static final String SEARCH_WIRED_BUNDLES = BundleResourceHelper.class.getName() + ".searchWiredBundles";
055 public static final String CONVERT_RESOURCE_URLS = BundleResourceHelper.class.getName() + ".convertResourceUrls";
056
057 private final static String META_INF_1 = "META-INF/";
058 private final static String META_INF_2 = "/META-INF/";
059
060 protected final Bundle bundle;
061 private LinkedHashSet<Bundle> wiredBundles = null;
062 protected boolean searchWiredBundles;
063 protected boolean convertResourceUrls;
064
065 public BundleResourceHelper(Bundle bundle) {
066 this(bundle,
067 BundleResourceHelper.getSearchWiredBundles(false),
068 BundleResourceHelper.getConvertResourceUrls(false));
069 }
070
071 public BundleResourceHelper(Bundle bundle, boolean searchWiredBundles, boolean convertResourceUrls) {
072 this.bundle = bundle;
073 this.searchWiredBundles = searchWiredBundles;
074 this.convertResourceUrls = convertResourceUrls;
075 }
076
077 public void setSearchWiredBundles(boolean search) {
078 searchWiredBundles = search;
079 }
080
081 public boolean getSearchWiredBundles() {
082 return searchWiredBundles;
083 }
084
085 public void setConvertResourceUrls(boolean convert) {
086 convertResourceUrls = convert;
087 }
088
089 public boolean getConvertResourceUrls() {
090 return convertResourceUrls;
091 }
092
093 public URL getResource(String name) {
094 if (convertResourceUrls) {
095 return convertedFindResource(name);
096 } else {
097 return findResource(name);
098 }
099 }
100
101 public Enumeration<URL> getResources(String name) throws IOException {
102 if (convertResourceUrls) {
103 return convertedFindResources(name);
104 } else {
105 return findResources(name);
106 }
107 }
108
109 protected URL convert(URL url) {
110 return url;
111 }
112
113 private synchronized LinkedHashSet<Bundle> getWiredBundles() {
114 if (wiredBundles == null) {
115 wiredBundles = BundleUtils.getWiredBundles((bundle instanceof DelegatingBundle) ? ((DelegatingBundle) bundle).getMainBundle() : bundle);
116 }
117 return wiredBundles;
118 }
119
120 private boolean isMetaInfResource(String name) {
121 return searchWiredBundles && name != null && (name.startsWith(META_INF_1) || name.startsWith(META_INF_2));
122 }
123
124 private List<URL> getList() {
125 if (convertResourceUrls) {
126 return new ArrayList<URL>() {
127 public boolean add(URL u) {
128 return super.add(convert(u));
129 }
130 };
131 } else {
132 return new ArrayList<URL>();
133 }
134 }
135
136 private void addToList(List<URL> list, Enumeration<URL> enumeration) {
137 if (enumeration != null) {
138 while (enumeration.hasMoreElements()) {
139 list.add(enumeration.nextElement());
140 }
141 }
142 }
143
144 protected URL findResource(String name) {
145 URL resource = bundle.getResource(name);
146 if (resource == null && isMetaInfResource(name)) {
147 LinkedHashSet<Bundle> wiredBundles = getWiredBundles();
148 Iterator<Bundle> iterator = wiredBundles.iterator();
149 while (iterator.hasNext() && resource == null) {
150 resource = iterator.next().getResource(name);
151 }
152 }
153 if (resource != null && convertResourceUrls) {
154 resource = convert(resource);
155 }
156 return resource;
157 }
158
159 protected Enumeration<URL> findResources(String name) throws IOException {
160 Enumeration<URL> e = (Enumeration<URL>) bundle.getResources(name);
161 if (isMetaInfResource(name)) {
162 List<URL> allResources = getList();
163 addToList(allResources, e);
164 LinkedHashSet<Bundle> wiredBundles = getWiredBundles();
165 for (Bundle wiredBundle : wiredBundles) {
166 Enumeration<URL> resources = wiredBundle.getResources(name);
167 addToList(allResources, resources);
168 }
169 return Collections.enumeration(allResources);
170 } else if (e == null) {
171 return Collections.enumeration(Collections.<URL>emptyList());
172 } else if (convertResourceUrls) {
173 List<URL> allResources = getList();
174 addToList(allResources, e);
175 return Collections.enumeration(allResources);
176 } else {
177 return e;
178 }
179 }
180
181 /**
182 * Lookup resource and return converted URL (in a generic way).
183 *
184 * @param name
185 * @return
186 */
187 protected URL convertedFindResource(String name) {
188 ServiceReference reference = bundle.getBundleContext().getServiceReference(PackageAdmin.class.getName());
189 PackageAdmin packageAdmin = (PackageAdmin) bundle.getBundleContext().getService(reference);
190 try {
191 List<URL> resources = findResources(packageAdmin, bundle, name, false);
192 if (resources.isEmpty() && isMetaInfResource(name)) {
193 LinkedHashSet<Bundle> wiredBundles = getWiredBundles();
194 Iterator<Bundle> iterator = wiredBundles.iterator();
195 while (iterator.hasNext() && resources.isEmpty()) {
196 Bundle wiredBundle = iterator.next();
197 resources = findResources(packageAdmin, wiredBundle, name, false);
198 }
199 }
200 return (resources.isEmpty()) ? null : resources.get(0);
201 } catch (Exception e) {
202 return null;
203 } finally {
204 bundle.getBundleContext().ungetService(reference);
205 }
206 }
207
208 /**
209 * Lookup resources and return converted URLs (in a generic way).
210 *
211 * @param name
212 * @return
213 */
214 protected Enumeration<URL> convertedFindResources(String name) throws IOException {
215 ServiceReference reference = bundle.getBundleContext().getServiceReference(PackageAdmin.class.getName());
216 PackageAdmin packageAdmin = (PackageAdmin) bundle.getBundleContext().getService(reference);
217 try {
218 List<URL> resources = findResources(packageAdmin, bundle, name, true);
219 if (isMetaInfResource(name)) {
220 LinkedHashSet<Bundle> wiredBundles = getWiredBundles();
221 for (Bundle wiredBundle : wiredBundles) {
222 resources.addAll(findResources(packageAdmin, wiredBundle, name, true));
223 }
224 }
225 return Collections.enumeration(resources);
226 } catch (Exception e) {
227 throw new IOException("Error discovering resources", e);
228 } finally {
229 bundle.getBundleContext().ungetService(reference);
230 }
231 }
232
233 private static List<URL> findResources(PackageAdmin packageAdmin,
234 Bundle bundle,
235 String name,
236 final boolean continueScanning) throws Exception {
237 BundleResourceFinder finder = new BundleResourceFinder(packageAdmin, bundle, "", name);
238 final List<URL> resources = new ArrayList<URL>();
239 finder.find(new BundleResourceFinder.ResourceFinderCallback() {
240
241 public boolean foundInDirectory(Bundle bundle, String baseDir, URL url) throws Exception {
242 resources.add(url);
243 return continueScanning;
244 }
245
246 public boolean foundInJar(Bundle bundle, String jarName, ZipEntry entry, InputStream inputStream) throws Exception {
247 URL jarURL = bundle.getEntry(jarName);
248 URL url = new URL("jar:" + jarURL.toString() + "!/" + entry.getName());
249 resources.add(url);
250 return continueScanning;
251 }
252 });
253 return resources;
254 }
255
256 public static boolean getSearchWiredBundles(boolean defaultValue) {
257 String value = System.getProperty(SEARCH_WIRED_BUNDLES);
258 return (value == null) ? defaultValue : Boolean.parseBoolean(value);
259 }
260
261 public static boolean getConvertResourceUrls(boolean defaultValue) {
262 String value = System.getProperty(CONVERT_RESOURCE_URLS);
263 return (value == null) ? defaultValue : Boolean.parseBoolean(value);
264 }
265 }