/* * Copyright 2002-2006 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.osgi.context; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import org.osgi.framework.Bundle; import org.springframework.core.io.AbstractResource; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** * Resource implementation for OSGi environments. Lazy evaluation of the * resource will be used. * * Understands the "bundle:" resource prefix for explicit loading of resources * from the bundle. When the bundle prefix is used the target resource must be * contained within the bundle (or attached fragments), the classpath is not * searched. * * @author Adrian Colyer * @author Costin Leau * */ public class OsgiBundleResource extends AbstractResource { public static final String BUNDLE_URL_PREFIX = "bundle:"; private static final char PREFIX_SEPARATOR = ':'; private static final String ABSOLUTE_PATH_PREFIX = "/"; private final Bundle bundle; private final String path; public OsgiBundleResource(Bundle bundle, String path) { Assert.notNull(bundle, "Bundle must not be null"); this.bundle = bundle; // check path Assert.notNull(path, "Path must not be null"); this.path = StringUtils.cleanPath(path); } /** * Return the path for this resource. */ public final String getPath() { return path; } /** * Return the bundle for this resource. */ public final Bundle getBundle() { return bundle; } /** * This implementation opens an InputStream for the given URL. It sets the * "UseCaches" flag to <code>false</code>, mainly to avoid jar file * locking on Windows. * * @see java.net.URL#openConnection() * @see java.net.URLConnection#setUseCaches(boolean) * @see java.net.URLConnection#getInputStream() */ public InputStream getInputStream() throws IOException { URLConnection con = getURL().openConnection(); con.setUseCaches(false); return con.getInputStream(); } /** * This implementation returns a URL for the underlying bundle resource. * * @see org.osgi.framework.Bundle#getEntry(String) * @see org.osgi.framework.Bundle#getResource(String) */ public URL getURL() throws IOException { URL url = null; if (this.path.startsWith(BUNDLE_URL_PREFIX)) { url = getResourceFromBundle(this.path.substring(BUNDLE_URL_PREFIX.length())); } else if (this.path.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX)) { url = getResourceFromBundleClasspath(this.path.substring(ResourceLoader.CLASSPATH_URL_PREFIX.length())); } // Costin: default fallback - take from resource classpath // TODO: should it matter if the path is absolute or not? else { url = getResourceFromBundleClasspath(this.path); } if (url == null) { throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist"); } return url; } /** * Resolves a resource from *this bundle only*. Only the bundle and its * attached fragments are searched for the given resource. * * @param bundleRelativePath * @return a URL to the returned resource or null if none is found * * @see org.osgi.framework.Bundle#getEntry(String) */ protected URL getResourceFromBundle(String bundleRelativePath) { return bundle.getEntry(bundleRelativePath); } /** * Resolves a resource from the bundle's classpath. This will find resources * in this bundle and also in imported packages from other bundles. * * @param bundleRelativePath * @return a URL to the returned resource or null if none is found * * @see org.osgi.framework.Bundle#getResource(String) */ protected URL getResourceFromBundleClasspath(String bundleRelativePath) { return bundle.getResource(bundleRelativePath); } /** * Determine if the given path is relative or absolute. * * @param locationPath * @return */ protected boolean isRelativePath(String locationPath) { return ((locationPath.indexOf(PREFIX_SEPARATOR) == -1) && !locationPath.startsWith(ABSOLUTE_PATH_PREFIX)); } /** * This implementation creates a OsgiBundleResource, applying the given path * relative to the path of the underlying resource of this descriptor. * * @see org.springframework.util.StringUtils#applyRelativePath(String, * String) */ public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(this.path, relativePath); return new OsgiBundleResource(this.bundle, pathToUse); } /** * This implementation returns the name of the file that this bundle path * resource refers to. * * @see org.springframework.util.StringUtils#getFilename(String) */ public String getFilename() { return StringUtils.getFilename(this.path); } /** * This implementation returns a description that includes the bundle * location. */ public String getDescription() { return "OSGi bundle resource [" + this.path + "]"; } /** * This implementation compares the underlying bundle and path locations. */ public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof OsgiBundleResource) { OsgiBundleResource otherRes = (OsgiBundleResource) obj; return (this.path.equals(otherRes.path) && ObjectUtils.nullSafeEquals(this.bundle, otherRes.bundle)); } return false; } /** * This implementation returns the hash code of the underlying class path * location. */ public int hashCode() { return this.path.hashCode(); } }