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.io.InputStream; 024import java.net.URL; 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.Enumeration; 028import java.util.Iterator; 029import java.util.LinkedHashSet; 030import java.util.List; 031import java.util.zip.ZipEntry; 032 033import org.osgi.framework.Bundle; 034import org.osgi.framework.ServiceReference; 035import 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: 1331428 $ $Date: 2012-04-27 15:39:19 +0200 (Fri, 27 Apr 2012) $ 051 */ 052public 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 (IOException) new IOException("Error discovering resources: " + e).initCause(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 = BundleUtils.getEntry(bundle, 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}