| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. The ASF licenses this file to You |
| * 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.apache.jackrabbit.classloader; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.security.cert.Certificate; |
| import java.util.Date; |
| import java.util.jar.Manifest; |
| |
| import javax.jcr.Property; |
| import javax.jcr.RepositoryException; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.jackrabbit.net.URLFactory; |
| |
| |
| /** |
| * The <code>ClassLoaderResource</code> class represents a resource looked up |
| * by the {@link ClassPathEntry}s of the {@link RepositoryClassLoader}. The |
| * class provides transparent access to the resource irrespective of the fact |
| * on whether the resource is contained in a repository property or in an |
| * JAR or ZIP archive. |
| * <p> |
| * This class is extended to implement depending features such as storing |
| * resources in repository properties or JAR or ZIP archives. |
| * |
| * @author Felix Meschberger |
| */ |
| class ClassLoaderResource { |
| |
| /** default log category */ |
| private static final Log log = LogFactory.getLog(ClassLoaderResource.class); |
| |
| /** |
| * The class path entry which loaded this class loader resource |
| */ |
| private final ClassPathEntry pathEntry; |
| |
| /** |
| * The name of this resource. |
| */ |
| private final String name; |
| |
| /** |
| * The repository property providing the resource's contents. This may be |
| * <code>null</code> if the resource was loaded from a JAR/ZIP archive. |
| */ |
| private final Property resProperty; |
| |
| /** |
| * The class optionally loaded/defined through this resource. |
| * |
| * @see #getLoadedClass() |
| * @see #setLoadedClass(Class) |
| */ |
| private Class loadedClass; |
| |
| /** |
| * The time in milliseconds at which this resource has been loaded from |
| * the repository. |
| */ |
| private final long loadTime; |
| |
| /** |
| * Creates an instance of this class for the class path entry. |
| * |
| * @param pathEntry The {@link ClassPathEntry} of the code source of this |
| * class loader resource. |
| * @param name The path name of this resource. |
| * @param resProperty The <code>Property</code>providing the content's of |
| * this resource. This may be <code>null</code> if the resource |
| * was loaded from an JAR or ZIP archive. |
| */ |
| /* package */ ClassLoaderResource(ClassPathEntry pathEntry, String name, |
| Property resProperty) { |
| this.pathEntry = pathEntry; |
| this.name = name; |
| this.resProperty = resProperty; |
| this.loadTime = System.currentTimeMillis(); |
| } |
| |
| /** |
| * Returns the {@link ClassPathEntry} which loaded this resource. |
| */ |
| protected ClassPathEntry getClassPathEntry() { |
| return pathEntry; |
| } |
| |
| /** |
| * Returns the name of this resource. This is the name used to find the |
| * resource, for example the class name or the properties file path. |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Returns the <code>Property</code> with which this resource is created. |
| */ |
| protected Property getProperty() { |
| return resProperty; |
| } |
| |
| /** |
| * Returns the time in milliseconds at which this resource has been loaded |
| */ |
| protected long getLoadTime() { |
| return loadTime; |
| } |
| |
| /** |
| * Returns the URL to access this resource, for example a JCR or a JAR URL. |
| * If the URL cannot be created from the resource data, <code>null</code> is |
| * returned. |
| */ |
| public URL getURL() { |
| try { |
| return URLFactory.createURL(getClassPathEntry().session, getPath()); |
| } catch (Exception e) { |
| log.warn("getURL: Cannot getURL for " + getPath(), e); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the URL to the code source of this entry. If there is no code |
| * source available, <code>null</code> is returned. |
| * <p> |
| * This base class implementation returns the result of calling |
| * {@link ClassPathEntry#toURL()} on the class path entry from which this |
| * resource was loaded. |
| */ |
| public URL getCodeSourceURL() { |
| return getClassPathEntry().toURL(); |
| } |
| |
| /** |
| * Returns an <code>InputStream</code> to read from the resource. |
| * <p> |
| * This base class implementation returns the result of calling the |
| * <code>getStream()</code> method on the resource's property or |
| * <code>null</code> if the property is not set. |
| */ |
| public InputStream getInputStream() throws RepositoryException { |
| return (getProperty() != null) ? getProperty().getStream() : null; |
| } |
| |
| /** |
| * Returns the size of the resource or -1 if the size cannot be found out. |
| * <p> |
| * This base class implementation returns the result of calling the |
| * <code>getLength()</code> method on the resource's property or -1 if |
| * the property is not set. |
| * |
| * @throws RepositoryException If an error occurrs trying to find the length |
| * of the property. |
| */ |
| public int getContentLength() throws RepositoryException { |
| return (getProperty() != null) ? (int) getProperty().getLength() : -1; |
| } |
| |
| /** |
| * Returns the path of the property containing the resource. |
| * <p> |
| * This base class implementation returns the absolute path of the |
| * resource's property. If the property is not set or if an error occurrs |
| * accesing the property's path, the concatentation of the class path |
| * entry's path and the resource's name is returned. |
| */ |
| public String getPath() { |
| if (getProperty() != null) { |
| try { |
| return getProperty().getPath(); |
| } catch (RepositoryException re) { |
| // fallback |
| log.warn("getPath: Cannot retrieve path of entry " + getName(), |
| re); |
| } |
| } |
| |
| // fallback if no resource property or an error accessing the path of |
| // the property |
| return getClassPathEntry().getPath() + getName(); |
| } |
| |
| /** |
| * Returns the time of the the last modification of the resource or -1 if |
| * the last modification time cannot be evaluated. |
| * <p> |
| * This base class implementation returns the result of calling the |
| * {@link Util#getLastModificationTime(Property)} method on the resource's |
| * property if not <code>null</code>. In case of an error or if the |
| * property is <code>null</code>, -1 is returned. |
| */ |
| public long getLastModificationTime() { |
| if (getProperty() != null) { |
| try { |
| return Util.getLastModificationTime(getProperty()); |
| } catch (RepositoryException re) { |
| log.info("getLastModificationTime of resource property", re); |
| } |
| } |
| |
| // cannot find the resource modification time, use epoch |
| return -1; |
| } |
| |
| /** |
| * Returns the resource as an array of bytes |
| */ |
| public byte[] getBytes() throws IOException, RepositoryException { |
| InputStream in = null; |
| byte[] buf = null; |
| |
| log.debug("getBytes"); |
| |
| try { |
| in = getInputStream(); |
| log.debug("getInputStream() returned " + in); |
| |
| int length = getContentLength(); |
| log.debug("getContentLength() returned " + String.valueOf(length)); |
| |
| if (length >= 0) { |
| |
| buf = new byte[length]; |
| for (int read; length > 0; length -= read) { |
| read = in.read(buf, buf.length - length, length); |
| if (read == -1) { |
| throw new IOException("unexpected EOF"); |
| } |
| } |
| |
| } else { |
| |
| buf = new byte[1024]; |
| int count = 0; |
| int read; |
| |
| // read enlarging buffer |
| while ((read = in.read(buf, count, buf.length - count)) != -1) { |
| count += read; |
| if (count >= buf.length) { |
| byte buf1[] = new byte[count * 2]; |
| System.arraycopy(buf, 0, buf1, 0, count); |
| buf = buf1; |
| } |
| } |
| |
| // resize buffer if too big |
| if (count != buf.length) { |
| byte buf1[] = new byte[count]; |
| System.arraycopy(buf, 0, buf1, 0, count); |
| buf = buf1; |
| } |
| |
| } |
| |
| } finally { |
| |
| if (in != null) { |
| try { |
| in.close(); |
| } catch (IOException ignore) { |
| } |
| } |
| |
| } |
| |
| return buf; |
| } |
| |
| /** |
| * Returns the manifest from the jar file for this class resource. If this |
| * resource is not from a jar file, the method returns <code>null</code>, |
| * which is what the default implementation does. |
| */ |
| public Manifest getManifest() { |
| return null; |
| } |
| |
| /** |
| * Returns the certificates from the jar file for this class resource. If |
| * this resource is not from a jar file, the method returns |
| * <code>null</code>, which is what the default implementation does. |
| */ |
| public Certificate[] getCertificates() { |
| return null; |
| } |
| |
| /** |
| * Returns the <code>Property</code> which is used to check whether this |
| * resource is expired or not. |
| * <p> |
| * This base class method returns the same property as returned by the |
| * {@link #getProperty()} method. This method may be overwritten by |
| * implementations as appropriate. |
| * |
| * @see #isExpired() |
| */ |
| protected Property getExpiryProperty() { |
| return getProperty(); |
| } |
| |
| /** |
| * Returns <code>true</code> if the last modification date of the expiry |
| * property of this resource is loaded is later than the time at which this |
| * resource has been loaded. If the last modification time of the expiry |
| * property cannot be calculated or if an error occurrs checking the expiry |
| * propertiy's last modification time, <code>true</code> is returned. |
| */ |
| public boolean isExpired() { |
| try { |
| // creation time of version if loaded now |
| long currentPropTime = 0; |
| Property prop = getExpiryProperty(); |
| if (prop != null) { |
| currentPropTime = Util.getLastModificationTime(prop); |
| } |
| |
| // creation time of version currently loaded |
| long loadTime = getLoadTime(); |
| |
| // expire if a new version would be loaded |
| boolean exp = currentPropTime > loadTime; |
| if (exp && log.isDebugEnabled()) { |
| log.debug("expireResource: Resource created " + |
| new Date(loadTime) + " superceded by version created " + |
| new Date(currentPropTime)); |
| } |
| |
| // return the result of checking |
| return exp; |
| } catch (RepositoryException re) { |
| log.debug("expireResource: Cannot get current version for " + |
| toString() + ", will expire: " + re); |
| return true; |
| } |
| } |
| |
| /** |
| * Returns the class which was loaded through this resource. It is expected |
| * that the class loader sets the class which was loaded through this |
| * resource by calling the {@link #setLoadedClass(Class)} method. If this |
| * class was not used to load a class or if the class loader failed to |
| * set the class loaded through this resoource, this method will return |
| * <code>null</code>. |
| * |
| * @return The class loaded through this resource, which may be |
| * <code>null</code> if this resource was never used to load a class |
| * or if the loader failed to set class through the |
| * {@link #setLoadedClass(Class)} method. |
| * |
| * @see #setLoadedClass(Class) |
| */ |
| public Class getLoadedClass() { |
| return loadedClass; |
| } |
| |
| /** |
| * Sets the class which was loaded through this resource. This method does |
| * not check, whether it is plausible that this class was actually loaded |
| * from this resource, nor does this method check whether the class object |
| * is <code>null</code> or not. |
| * |
| * @param loadedClass The class to be loaded. |
| */ |
| public void setLoadedClass(Class loadedClass) { |
| this.loadedClass = loadedClass; |
| } |
| |
| /** |
| * Returns the <code>String</code> representation of this resource. |
| */ |
| public String toString() { |
| StringBuffer buf = new StringBuffer(getClass().getName()); |
| buf.append(": path="); |
| buf.append(getPath()); |
| buf.append(", name="); |
| buf.append(getName()); |
| return buf.toString(); |
| } |
| } |