| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * 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.sling.jcr.classloader.internal; |
| |
| 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.sling.jcr.classloader.internal.net.URLFactory; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| /** |
| * The <code>ClassLoaderResource</code> class represents a resource looked up |
| * by the {@link ClassPathEntry}s of the {@link URLRepositoryClassLoader}. |
| */ |
| class ClassLoaderResource { |
| |
| /** default log category */ |
| private static final Logger log = |
| LoggerFactory.getLogger(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; |
| |
| /** |
| * Flag indicating that this resource has already been checked for expiry |
| * and whether it is actually expired. |
| * |
| * @see #isExpired() |
| */ |
| private boolean expired; |
| |
| /** |
| * 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 getSafePath(); |
| } |
| |
| /** |
| * Returns the path of the property containing the resource by appending |
| * the {@link #getName() name} to the path of the class path entry to which |
| * this resource belongs. This path need not necessairily be the same as |
| * the {@link #getProperty() path of the property} but will always succeed |
| * as there is no repository access involved. |
| */ |
| protected String getSafePath() { |
| 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 {}", new Integer(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() { |
| if (!expired) { |
| // creation time of version if loaded now |
| long currentPropTime = 0; |
| Property prop = getExpiryProperty(); |
| if (prop != null) { |
| try { |
| currentPropTime = Util.getLastModificationTime(prop); |
| } catch (RepositoryException re) { |
| // cannot get last modif time from property, use current time |
| log.debug("expireResource: Cannot get current version for " |
| + toString() + ", will expire", re); |
| currentPropTime = System.currentTimeMillis(); |
| } |
| } |
| |
| // creation time of version currently loaded |
| long loadTime = getLoadTime(); |
| |
| // expire if a new version would be loaded |
| expired = currentPropTime > loadTime; |
| if (expired && log.isDebugEnabled()) { |
| log.debug( |
| "expireResource: Resource created {} superceded by version created {}", |
| new Date(loadTime), new Date(currentPropTime)); |
| } |
| } |
| |
| return expired; |
| } |
| |
| /** |
| * 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() { |
| final StringBuilder buf = new StringBuilder(getClass().getName()); |
| buf.append(": path="); |
| buf.append(getSafePath()); |
| buf.append(", name="); |
| buf.append(getName()); |
| return buf.toString(); |
| } |
| } |