| /* |
| * 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.MalformedURLException; |
| import java.net.URL; |
| import java.security.AccessControlException; |
| import java.util.jar.JarException; |
| import java.util.jar.JarInputStream; |
| |
| import javax.jcr.ItemNotFoundException; |
| import javax.jcr.PathNotFoundException; |
| import javax.jcr.Property; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.jackrabbit.net.URLFactory; |
| |
| /** |
| * The <code>ClassPathEntry</code> class encapsulates entries in the class path |
| * of the {@link DynamicRepositoryClassLoader}. The main task is to retrieve |
| * {@link ClassLoaderResource} instances for classes or resources to load from it. |
| * <p> |
| * This implementation is not currently integrated with Java security. That is |
| * protection domains and security managers are not supported yet. |
| * <p> |
| * This class is not intended to be subclassed or instantiated by clients. |
| * |
| * @author Felix Meschberger |
| */ |
| abstract class ClassPathEntry { |
| |
| /** default logging */ |
| private static final Log log = LogFactory.getLog(ClassPathEntry.class); |
| |
| /** The session assigned to this class path entry */ |
| protected final Session session; |
| |
| /** The path to the item of this class path entry */ |
| protected final String path; |
| |
| /** The base URL for the class path entry to later construct resource URLs */ |
| protected URL baseURL; |
| |
| //---------- construction -------------------------------------------------- |
| |
| /** |
| * Creates an instance of the <code>ClassPathEntry</code> assigning the |
| * session and path. |
| * |
| * @param session The <code>Session</code> to access the Repository. |
| * @param path The path of the class path entry, this is either the |
| * path of a node containing a jar archive or is the path |
| * of the root of a hierarchy to look up resources in. |
| */ |
| protected ClassPathEntry(Session session, String path) { |
| this.path = path; |
| this.session = session; |
| } |
| |
| /** |
| * Clones this instance of the <code>ClassPathEntry</code> setting the |
| * path and session to the same value as the base instance. |
| * <p> |
| * Note that this constructor does not duplicate the session from the base |
| * instance. |
| * |
| * @param base The <code>ClassPathEntry</code> from which to copy the path |
| * and the session. |
| */ |
| protected ClassPathEntry(ClassPathEntry base) { |
| this.path = base.path; |
| this.session = base.session; |
| this.baseURL = base.baseURL; |
| } |
| |
| /** |
| * Returns an instance of the <code>ClassPathEntry</code> class. This |
| * instance will be a subclass correctly handling the type (directory or |
| * jar archive) of class path entry is to be created. |
| * <p> |
| * If the path given has a trailing slash, it is taken as a directory root |
| * else the path is first tested, whether it contains an archive. If not |
| * the path is treated as a directory. |
| * |
| * @param session The <code>Session</code> to access the Repository. |
| * @param path The path of the class path entry, this is either the |
| * path of a node containing a jar archive or is the path |
| * of the root of a hierharchy to look up resources in. |
| * |
| * @return An initialized <code>ClassPathEntry</code> instance for the |
| * path or <code>null</code> if an error occurred creating the |
| * instance. |
| */ |
| static ClassPathEntry getInstance(Session session, String path) { |
| |
| // check we can access the path, don't care about content now |
| try { |
| session.checkPermission(path, "read"); |
| } catch (AccessControlException ace) { |
| log.warn("getInstance: Access denied reading from " + path + |
| ", ignoring entry"); |
| return null; |
| } catch (RepositoryException re) { |
| log.error("getInstance: Cannot check permission to " + path, re); |
| } |
| |
| // only check for archive if no trailing slash in path |
| if (!path.endsWith("/")) { |
| InputStream is = null; |
| JarInputStream zip = null; |
| try { |
| |
| Property prop = Util.getProperty(session.getItem(path)); |
| if (prop != null) { |
| |
| is = prop.getStream(); |
| zip = new JarInputStream(is); |
| if (zip.getNextJarEntry() != null /* && zip.read() != -1 */ ) { |
| // use the expanding jar support if can expand |
| if (ExpandingArchiveClassPathEntry.canExpandArchives(session)) { |
| return new ExpandingArchiveClassPathEntry(prop, path); |
| } |
| |
| // otherwise use the non-expanding |
| return new ArchiveClassPathEntry(prop, path); |
| } |
| |
| log.debug("getInstance: " + path + " might not be a jar " + |
| "archive, using as directory"); |
| } else { |
| log.debug("getInstance: " + path + " does not resolve" + |
| " to a property, using as directory"); |
| } |
| |
| } catch (ItemNotFoundException infe) { |
| |
| // how to path ? |
| // thrown from |
| // - Node.getPrimaryItem |
| // - |
| |
| } catch (PathNotFoundException pnfe) { |
| |
| // how to path ? |
| // thrown from |
| // - session.getItem |
| // - |
| |
| } catch (RepositoryException re) { |
| |
| log.debug("getInstance: " + path + " cannot be read from, " + |
| "using as directory"); |
| |
| } catch (JarException ze) { |
| |
| log.debug("getInstance: " + path + " does not contain an " + |
| "archive, using as directory"); |
| |
| } catch (IOException ioe) { |
| |
| log.debug("getInstance: " + path + " problem reading from " + |
| "the archive, using as directory"); |
| |
| } finally { |
| if (zip != null) { |
| try { |
| zip.close(); |
| } catch (IOException ignored) {} |
| } else if (is != null) { |
| try { |
| is.close(); |
| } catch (IOException ignored) {} |
| } |
| } |
| // assume the path designates a directory |
| |
| // append trailing slash now |
| path += "/"; |
| } |
| |
| // we assume a directory class path entry, but we might have to check |
| // whether the path refers to a node or not. On the other hande, this |
| // class path entry will not be usable anyway if not, user beware :-) |
| |
| return new DirectoryClassPathEntry(session, path); |
| } |
| |
| /** |
| * Returns the path on which this <code>ClassPathEntry</code> is based. |
| */ |
| public String getPath() { |
| return path; |
| } |
| |
| /** |
| * Returns this <code>ClassPathEntry</code> represented as an URL to be |
| * used in a list of URLs to further work on. If there is a problem creating |
| * the URL for this instance, <code>null</code> is returned instead. |
| */ |
| public URL toURL() { |
| if (baseURL == null) { |
| try { |
| baseURL = URLFactory.createURL(session, path); |
| } catch (MalformedURLException mue) { |
| log.warn("DirectoryClassPathEntry: Creating baseURl for " + |
| path, mue); |
| } |
| } |
| |
| return baseURL; |
| } |
| |
| /** |
| * Returns a <code>ClassPathEntry</code> with the same configuration as |
| * this <code>ClassPathEntry</code>. |
| * <p> |
| * The returned object may be but need not be a new instance. If the original |
| * implementation is an immutable class, the instance returned may well |
| * be the same as this. |
| */ |
| abstract ClassPathEntry copy(); |
| |
| /** |
| * Searches for the named resource. The name is looked up as is, it is not |
| * further modified such as appended with ".class" or made relative. That |
| * is callers must make sure, that (1) this name is the full name of the |
| * resource to find and that (2) it is a relative name, that is it should |
| * not have a leading slash. |
| * <p> |
| * An example of a class to find would be : <code>com/day/test/Tester.class</code> |
| * which is converted from the generally used value <code>com.day.test.Tester</code> |
| * by the caller. |
| * |
| * @param name The name of the resource to find. |
| */ |
| public abstract ClassLoaderResource getResource(String name); |
| |
| /** |
| * @see Object#toString() |
| */ |
| public String toString() { |
| StringBuffer buf = new StringBuffer(super.toString()); |
| buf.append(": path: "); |
| buf.append(path); |
| buf.append(", user: "); |
| buf.append(session.getUserID()); |
| return buf.toString(); |
| } |
| |
| //----------- internal helper ---------------------------------------------- |
| |
| } |