You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:47:01 UTC
[sling-org-apache-sling-jcr-classloader] 13/18: SLING-1316 :
Include jackrabbit classloader code to adjust it for Sling needs: Remove
unused code and combine the classloader classes into a single class.
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.jcr.classloader-3.1.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-classloader.git
commit eaea87ba6c113f871c4a53c3888a53a3f812cd85
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Jan 27 09:30:55 2010 +0000
SLING-1316 : Include jackrabbit classloader code to adjust it for Sling needs:
Remove unused code and combine the classloader classes into a single class.
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/jcr/classloader@903578 13f79535-47bb-0310-9956-ffa450edef68
---
.../jcr/classloader/internal/ClassPathEntry.java | 12 -
.../internal/DynamicRepositoryClassLoader.java | 833 +++++++++++++--------
.../internal/URLRepositoryClassLoader.java | 794 --------------------
3 files changed, 532 insertions(+), 1107 deletions(-)
diff --git a/src/main/java/org/apache/sling/jcr/classloader/internal/ClassPathEntry.java b/src/main/java/org/apache/sling/jcr/classloader/internal/ClassPathEntry.java
index 3f79345..99ef875 100644
--- a/src/main/java/org/apache/sling/jcr/classloader/internal/ClassPathEntry.java
+++ b/src/main/java/org/apache/sling/jcr/classloader/internal/ClassPathEntry.java
@@ -199,18 +199,6 @@ public final class ClassPathEntry {
}
/**
- * Returns a <code>ClassPathEntry</code> with the same configuration as
- * this <code>ClassPathEntry</code>.
- * <p>
- * Becase the <code>DirectoryClassPathEntry</code> class does not have
- * internal state, this method returns this instance to be used as
- * the "copy".
- */
- ClassPathEntry copy() {
- return this;
- }
-
- /**
* @see Object#toString()
*/
public String toString() {
diff --git a/src/main/java/org/apache/sling/jcr/classloader/internal/DynamicRepositoryClassLoader.java b/src/main/java/org/apache/sling/jcr/classloader/internal/DynamicRepositoryClassLoader.java
index 34cfc5f..3b38b95 100644
--- a/src/main/java/org/apache/sling/jcr/classloader/internal/DynamicRepositoryClassLoader.java
+++ b/src/main/java/org/apache/sling/jcr/classloader/internal/DynamicRepositoryClassLoader.java
@@ -16,10 +16,24 @@
*/
package org.apache.sling.jcr.classloader.internal;
-import java.util.Arrays;
+import java.beans.Introspector;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
@@ -34,8 +48,7 @@ import org.slf4j.LoggerFactory;
/**
- * The <code>DynamicRepositoryClassLoader</code> class extends the
- * {@link org.apache.sling.jcr.classloader.internal.URLRepositoryClassLoader} and provides the
+ * The <code>DynamicRepositoryClassLoader</code> class provides the
* functionality to load classes and resources from the JCR Repository.
* Additionally, this class supports the notion of getting 'dirty', which means,
* that if a resource loaded through this class loader has been modified in the
@@ -50,27 +63,30 @@ import org.slf4j.LoggerFactory;
* list of path patterns as was used to create the internal class path for the
* original class loader. The resulting internal class path need not be the
* same, though.
- * <p>
- * As an additional feature the class loaders provides the functionality for
- * complete reconfiguration of the list of path patterns defined at class loader
- * construction time through the {@link #reconfigure(String[])} method. This
- * reconfiguration replaces the internal class path with a new one built from
- * the new path list and also replaces that path list. Reinstantiating a
- * reconfigured class loader gets a class loader containing the same path list
- * as the original class loader had after reconfiguration. That is the original
- * configuration is lost. While reconfiguration is not able to throw away
- * classes already loaded, it will nevertheless mark the class loader dirty, if
- * any classes have already been loaded through it.
- * <p>
- * This class is not intended to be extended by clients.
*/
-public class DynamicRepositoryClassLoader
- extends URLRepositoryClassLoader
- implements EventListener {
+public final class DynamicRepositoryClassLoader
+ extends URLClassLoader implements EventListener {
+
+ /** An empty list of url paths to call superclass constructor */
+ private static final URL[] NULL_PATH = {};
+
+ /**
+ * The special resource representing a resource which could not be
+ * found in the class path.
+ *
+ * @see #cache
+ * @see #findClassLoaderResource(String)
+ */
+ private static final ClassLoaderResource NOT_FOUND_RESOURCE =
+ new ClassLoaderResource(null, "[sentinel]", null) {
+ public boolean isExpired() {
+ return false;
+ }
+ };
+
/** default log category */
- private final Logger log =
- LoggerFactory.getLogger(this.getClass().getName());
+ private final Logger log = LoggerFactory.getLogger(this.getClass().getName());
/**
* Cache of resources used to check class loader expiry. The map is indexed
@@ -83,21 +99,57 @@ public class DynamicRepositoryClassLoader
* @see #onEvent(EventIterator)
* @see #findClassLoaderResource(String)
*/
- private Map<String, ClassLoaderResource> modTimeCache;
+ private final Map<String, ClassLoaderResource> modTimeCache = new HashMap<String, ClassLoaderResource>();
/**
* Flag indicating whether there are loaded classes which have later been
* expired (e.g. invalidated or modified)
*/
- private boolean dirty;
+ private boolean dirty = false;
+
+ /** The registered event listeners. */
+ private EventListener[] proxyListeners;
/**
- * The list of repositories added through either the {@link #addURL} or the
- * {@link #addHandle} method.
+ * The classpath which this classloader searches for class definitions.
+ * Each element of the vector should be either a directory, a .zip
+ * file, or a .jar file.
+ * <p>
+ * It may be empty when only system classes are controlled.
*/
- private ClassPathEntry[] addedRepositories;
+ private ClassPathEntry[] repository;
- private EventListener[] proxyListeners;
+ /**
+ * The list of paths to use as a classpath.
+ */
+ private String[] paths;
+
+ /**
+ * The <code>Session</code> grants access to the Repository to access the
+ * resources.
+ * <p>
+ * This field is not final such that it may be cleared when the class loader
+ * is destroyed.
+ */
+ private Session session;
+
+ /**
+ * Cache of resources found or not found in the class path. The map is
+ * indexed by resource name and contains mappings to instances of the
+ * {@link ClassLoaderResource} class. If a resource has been tried to be
+ * loaded, which could not be found, the resource is cached with the
+ * special mapping to {@link #NOT_FOUND_RESOURCE}.
+ *
+ * @see #NOT_FOUND_RESOURCE
+ * @see #findClassLoaderResource(String)
+ */
+ private final Map<String, ClassLoaderResource> cache = new HashMap<String, ClassLoaderResource>();
+
+ /**
+ * Flag indicating whether the {@link #destroy()} method has already been
+ * called (<code>true</code>) or not (<code>false</code>)
+ */
+ private boolean destroyed = false;
/**
* Creates a <code>DynamicRepositoryClassLoader</code> from a list of item
@@ -114,18 +166,29 @@ public class DynamicRepositoryClassLoader
* @throws NullPointerException if either the session or the handles list
* is <code>null</code>.
*/
- public DynamicRepositoryClassLoader(Session session,
- String[] classPath, ClassLoader parent) {
-
+ public DynamicRepositoryClassLoader(final Session session,
+ final String[] classPath,
+ final ClassLoader parent) {
// initialize the super class with an empty class path
- super(session, classPath, parent);
+ super(NULL_PATH, parent);
+
+ // check session and handles
+ if (session == null) {
+ throw new NullPointerException("session");
+ }
+ if (classPath == null || classPath.length == 0) {
+ throw new NullPointerException("handles");
+ }
// set fields
- dirty = false;
- modTimeCache = new HashMap<String, ClassLoaderResource>();
+ this.session = session;
+ this.paths = classPath;
+
+ // build the class repositories list
+ buildRepository();
// register with observation service and path pattern list
- registerModificationListener();
+ registerListeners();
log.debug("DynamicRepositoryClassLoader: {} ready", this);
}
@@ -144,23 +207,25 @@ public class DynamicRepositoryClassLoader
* @param parent The parent <code>ClassLoader</code>, which may be
* <code>null</code>.
*/
- private DynamicRepositoryClassLoader(Session session,
- DynamicRepositoryClassLoader old, ClassLoader parent) {
-
+ private DynamicRepositoryClassLoader(final Session session,
+ final DynamicRepositoryClassLoader old,
+ final ClassLoader parent) {
// initialize the super class with an empty class path
- super(session, old.getPaths(), parent);
+ super(NULL_PATH, parent);
- // set the configuration and fields
- dirty = false;
- modTimeCache = new HashMap<String, ClassLoaderResource>();
+ // check session and handles
+ if (session == null) {
+ throw new NullPointerException("session");
+ }
+ // set fields
+ this.session = session;
+ this.paths = old.paths;
- // create a repository from the handles - might get a different one
- setRepository(resetClassPathEntries(old.getRepository()));
- setAddedRepositories(resetClassPathEntries(old.getAddedRepositories()));
+ repository = old.repository;
buildRepository();
// register with observation service and path pattern list
- registerModificationListener();
+ registerListeners();
// finally finalize the old class loader
old.destroy();
@@ -181,119 +246,450 @@ public class DynamicRepositoryClassLoader
*/
public void destroy() {
// we expect to be called only once, so we stop destroyal here
- if (isDestroyed()) {
+ if (destroyed) {
log.debug("Instance is already destroyed");
return;
}
// remove ourselves as listeners from other places
- unregisterListener();
-
- addedRepositories = null;
-
- super.destroy();
+ unregisterListeners();
+
+ // set destroyal guard
+ destroyed = true;
+
+ // clear caches and references
+ repository = null;
+ paths = null;
+ session = null;
+
+ // clear the cache of loaded resources and flush cached class
+ // introspections of the JavaBean framework
+ final Iterator<ClassLoaderResource> ci = cache.values().iterator();
+ while ( ci.hasNext() ) {
+ final ClassLoaderResource res = ci.next();
+ if (res.getLoadedClass() != null) {
+ Introspector.flushFromCaches(res.getLoadedClass());
+ res.setLoadedClass(null);
+ }
+ }
+ cache.clear();
+ modTimeCache.clear();
}
- //---------- reload support ------------------------------------------------
+ //---------- URLClassLoader overwrites -------------------------------------
/**
- * Checks whether this class loader already loaded the named resource and
- * would load another version if it were instructed to do so. As a side
- * effect the class loader sets itself dirty in this case.
- * <p>
- * Calling this method yields the same result as calling
- * {@link #shouldReload(String, boolean)} with the <code>force</code>
- * argument set to <code>false</code>.
+ * Finds and loads the class with the specified name from the class path.
*
- * @param name The name of the resource to check.
+ * @param name the name of the class
+ * @return the resulting class
*
- * @return <code>true</code> if the resource is loaded and reloading would
- * take another version than currently loaded.
+ * @throws ClassNotFoundException If the named class could not be found or
+ * if this class loader has already been destroyed.
+ */
+ protected Class<?> findClass(final String name) throws ClassNotFoundException {
+
+ if (destroyed) {
+ throw new ClassNotFoundException(name + " (Classloader destroyed)");
+ }
+
+ log.debug("findClass: Try to find class {}", name);
+
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Class<?>>() {
+
+ public Class<?> run() throws ClassNotFoundException {
+ return findClassPrivileged(name);
+ }
+ });
+ } catch (java.security.PrivilegedActionException pae) {
+ throw (ClassNotFoundException) pae.getException();
+ }
+ }
+
+ /**
+ * Finds the resource with the specified name on the search path.
+ *
+ * @param name the name of the resource
*
- * @see #isDirty
+ * @return a <code>URL</code> for the resource, or <code>null</code>
+ * if the resource could not be found or if the class loader has
+ * already been destroyed.
*/
- public synchronized boolean shouldReload(String name) {
- return shouldReload(name, false);
+ public URL findResource(String name) {
+
+ if (destroyed) {
+ log.warn("Destroyed class loader cannot find a resource");
+ return null;
+ }
+
+ log.debug("findResource: Try to find resource {}", name);
+
+ ClassLoaderResource res = findClassLoaderResource(name);
+ if (res != null) {
+ log.debug("findResource: Getting resource from {}, created {}",
+ res, new Date(res.getLastModificationTime()));
+ return res.getURL();
+ }
+
+ return null;
}
/**
- * Checks whether this class loader already loaded the named resource and
- * whether the class loader should be set dirty depending on the
- * <code>force</code> argument. If the argument is <code>true</code>, the
- * class loader is marked dirty and <code>true</code> is returned if the
- * resource has been loaded, else the loaded resource is checked for expiry
- * and the class loader is only set dirty if the loaded resource has
- * expired.
+ * Returns an Enumeration of URLs representing all of the resources
+ * on the search path having the specified name.
*
- * @param name The name of the resource to check.
- * @param force <code>true</code> if the class loader should be marked dirty
- * if the resource is loaded, else the class loader is only marked
- * dirty if the resource is loaded and has expired.
+ * @param name the resource name
*
- * @return <code>true</code> if the resource is loaded and
- * <code>force</code> is <code>true</code> or if the resource has
- * expired. <code>true</code> is also returned if this class loader
- * has already been destroyed.
+ * @return an <code>Enumeration</code> of <code>URL</code>s. This is an
+ * empty enumeration if no resources are found by this class loader
+ * or if this class loader has already been destroyed.
+ */
+ public Enumeration<URL> findResources(String name) {
+
+ if (destroyed) {
+ log.warn("Destroyed class loader cannot find resources");
+ return new Enumeration<URL>() {
+ public boolean hasMoreElements() {
+ return false;
+ }
+ public URL nextElement() {
+ throw new NoSuchElementException("No Entries");
+ }
+ };
+ }
+
+ log.debug("findResources: Try to find resources for {}", name);
+
+ List<URL> list = new LinkedList<URL>();
+ for (int i=0; i < repository.length; i++) {
+ final ClassPathEntry cp = repository[i];
+ log.debug("findResources: Trying {}", cp);
+
+ ClassLoaderResource res = cp.getResource(name);
+ if (res != null) {
+ log.debug("findResources: Adding resource from {}, created {}",
+ res, new Date(res.getLastModificationTime()));
+ URL url = res.getURL();
+ if (url != null) {
+ list.add(url);
+ }
+ }
+
+ }
+
+ // return the enumeration on the list
+ return Collections.enumeration(list);
+ }
+
+ /**
+ * Returns the search path of URLs for loading classes and resources.
+ * This includes the original list of URLs specified to the constructor,
+ * along with any URLs subsequently appended by the {@link #addURL(URL)}.
*
- * @see #isDirty
+ * @return the search path of URLs for loading classes and resources. The
+ * list is empty, if this class loader has already been destroyed.
+ * @see java.net.URLClassLoader#getURLs()
*/
- public synchronized boolean shouldReload(String name, boolean force) {
- if (isDestroyed()) {
- log.warn("Classloader already destroyed, reload required");
- return true;
+ public URL[] getURLs() {
+ if (destroyed) {
+ log.warn("Destroyed class loader has no URLs any more");
+ return NULL_PATH;
}
- ClassLoaderResource res = getCachedResource(name);
- if (res != null) {
- log.debug("shouldReload: Expiring cache entry {}", res);
- if (force) {
- log.debug("shouldReload: Forced dirty flag");
- dirty = true;
- return true;
+ List<URL> urls = new ArrayList<URL>();
+ for (int i=0; i < repository.length; i++) {
+ URL url = repository[i].toURL();
+ if (url != null) {
+ urls.add(url);
+ }
+ }
+ return urls.toArray(new URL[urls.size()]);
+ }
+
+ /**
+ * We don't allow to add new urls at runtime.
+ * @see java.net.URLClassLoader#addURL(java.net.URL)
+ */
+ protected void addURL(URL url) {
+ if (destroyed) {
+ log.warn("Cannot add URL to destroyed class loader");
+ } else {
+ log.warn("addURL: {} unable to add URL at runtime, ignored", url);
+ }
+ }
+
+ //---------- Property access ----------------------------------------------
+
+ /**
+ * Removes all entries from the cache of loaded resources, which mark
+ * resources, which have not been found as of yet.
+ *
+ * @throws NullPointerException If this class loader has already been
+ * destroyed.
+ */
+ private void cleanCache() {
+ final Iterator<ClassLoaderResource> ci = this.cache.values().iterator();
+ while (ci.hasNext()) {
+ if (ci.next() == NOT_FOUND_RESOURCE) {
+ ci.remove();
}
+ }
+ }
+
+ //---------- internal ------------------------------------------------------
- return expireResource(res);
+ /**
+ * Builds the repository list from the list of path patterns and appends
+ * the path entries from any added handles. This method may be used multiple
+ * times, each time replacing the currently defined repository list.
+ *
+ * @throws NullPointerException If this class loader has already been
+ * destroyed.
+ */
+ private synchronized void buildRepository() {
+ List<ClassPathEntry> newRepository = new ArrayList<ClassPathEntry>(paths.length);
+
+ // build repository from path patterns
+ for (int i=0; i < paths.length; i++) {
+ final String entry = paths[i];
+ ClassPathEntry cp = null;
+
+ // try to find repository based on this path
+ if (repository != null) {
+ for (int j=0; j < repository.length; j++) {
+ final ClassPathEntry tmp = repository[i];
+ if (tmp.getPath().equals(entry)) {
+ cp = tmp;
+ break;
+ }
+ }
+ }
+
+ // not found, creating new one
+ if (cp == null) {
+ cp = ClassPathEntry.getInstance(session, entry);
+ }
+
+ if (cp != null) {
+ log.debug("Adding path {}", entry);
+ newRepository.add(cp);
+ } else {
+ log.debug("Cannot get a ClassPathEntry for {}", entry);
+ }
}
- return false;
+ // replace old repository with new one
+ ClassPathEntry[] newClassPath = new ClassPathEntry[newRepository.size()];
+ newRepository.toArray(newClassPath);
+ repository = newClassPath;
+
+ // clear un-found resource cache
+ cleanCache();
}
/**
- * Returns <code>true</code> if any of the loaded classes need reload. Also
- * sets this class loader dirty. If the class loader is already set dirty
- * or if this class loader has been destroyed before calling this method,
- * it returns immediately.
+ * Tries to find the class in the class path from within a
+ * <code>PrivilegedAction</code>. Throws <code>ClassNotFoundException</code>
+ * if no class can be found for the name.
*
- * @return <code>true</code> if any class loader needs to be reinstantiated.
+ * @param name the name of the class
*
- * @see #isDirty
+ * @return the resulting class
+ *
+ * @throws ClassNotFoundException if the class could not be found
+ * @throws NullPointerException If this class loader has already been
+ * destroyed.
*/
- public synchronized boolean shouldReload() {
+ private Class<?> findClassPrivileged(String name) throws ClassNotFoundException {
+
+ // prepare the name of the class
+ final String path = name.replace('.', '/').concat(".class");
+ log.debug("findClassPrivileged: Try to find path {} for class {}",
+ path, name);
- // check whether we are already dirty
- if (isDirty()) {
- log.debug("shouldReload: Dirty, need reload");
- return true;
+ ClassLoaderResource res = findClassLoaderResource(path);
+ if (res != null) {
+
+ // try defining the class, error aborts
+ try {
+ log.debug(
+ "findClassPrivileged: Loading class from {}, created {}",
+ res, new Date(res.getLastModificationTime()));
+
+ Class<?> c = defineClass(name, res);
+ if (c == null) {
+ log.warn("defineClass returned null for class {}", name);
+ throw new ClassNotFoundException(name);
+ }
+ return c;
+
+ } catch (IOException ioe) {
+ log.debug("defineClass failed", ioe);
+ throw new ClassNotFoundException(name, ioe);
+ } catch (Throwable t) {
+ log.debug("defineClass failed", t);
+ throw new ClassNotFoundException(name, t);
+ }
+ }
+
+ throw new ClassNotFoundException(name);
+ }
+
+ /**
+ * Returns a {@link ClassLoaderResource} for the given <code>name</code> or
+ * <code>null</code> if not existing. If the resource has already been
+ * loaded earlier, the cached instance is returned. If the resource has
+ * not been found in an earlier call to this method, <code>null</code> is
+ * returned. Otherwise the resource is looked up in the class path. If
+ * found, the resource is cached and returned. If not found, the
+ * {@link #NOT_FOUND_RESOURCE} is cached for the name and <code>null</code>
+ * is returned.
+ *
+ * @param name The name of the resource to return.
+ *
+ * @return The named <code>ClassLoaderResource</code> if found or
+ * <code>null</code> if not found.
+ *
+ * @throws NullPointerException If this class loader has already been
+ * destroyed.
+ */
+ private ClassLoaderResource findClassLoaderResource(String name) {
+
+ // check for cached resources first
+ ClassLoaderResource res = cache.get(name);
+ if (res == NOT_FOUND_RESOURCE) {
+ log.debug("Resource '{}' known to not exist in class path", name);
+ return null;
+ } else if (res == null) {
+ // walk the repository list and try to find the resource
+ for (int i = 0; i < repository.length; i++) {
+ final ClassPathEntry cp = repository[i];
+ log.debug("Checking {}", cp);
+
+ res = cp.getResource(name);
+ if (res != null) {
+ log.debug("Found resource in {}, created ", res, new Date(
+ res.getLastModificationTime()));
+ cache.put(name, res);
+ break;
+ }
+ }
+ if ( res == null ) {
+ log.debug("No classpath entry contains {}", name);
+ cache.put(name, NOT_FOUND_RESOURCE);
+ return null;
+ }
+ }
+ // if it could be found, we register it with the caches
+ // register the resource in the expiry map, if an appropriate
+ // property is available
+ Property prop = res.getExpiryProperty();
+ if (prop != null) {
+ try {
+ modTimeCache.put(prop.getPath(), res);
+ } catch (RepositoryException re) {
+ log.warn("Cannot register the resource " + res +
+ " for expiry", re);
+ }
}
+ // and finally return the resource
+ return res;
+ }
- // Check whether any class has changed
- for (Iterator<ClassLoaderResource> iter = getCachedResources(); iter.hasNext();) {
- if (expireResource(iter.next())) {
- log.debug("shouldReload: Found expired resource, need reload");
- return true;
+ /**
+ * Defines a class getting the bytes for the class from the resource
+ *
+ * @param name The fully qualified class name
+ * @param res The resource to obtain the class bytes from
+ *
+ * @throws RepositoryException If a problem occurrs getting at the data.
+ * @throws IOException If a problem occurrs reading the class bytes from
+ * the resource.
+ * @throws ClassFormatError If the class bytes read from the resource are
+ * not a valid class.
+ */
+ private Class<?> defineClass(String name, ClassLoaderResource res)
+ throws IOException, RepositoryException {
+
+ log.debug("defineClass({}, {})", name, res);
+
+ Class<?> clazz = res.getLoadedClass();
+ if (clazz == null) {
+
+ /**
+ * This following code for packages is duplicate from URLClassLoader
+ * because it is private there. I would like to not be forced to
+ * do this, but I still have to find a way ... -fmeschbe
+ */
+
+ // package support
+ int i = name.lastIndexOf('.');
+ if (i != -1) {
+ String pkgname = name.substring(0, i);
+ // Check if package already loaded.
+ Package pkg = getPackage(pkgname);
+ URL url = res.getCodeSourceURL();
+ Manifest man = res.getManifest();
+ if (pkg != null) {
+ // Package found, so check package sealing.
+ boolean ok;
+ if (pkg.isSealed()) {
+ // Verify that code source URL is the same.
+ ok = pkg.isSealed(url);
+ } else {
+ // Make sure we are not attempting to seal the package
+ // at this code source URL.
+ ok = (man == null) || !isSealed(pkgname, man);
+ }
+ if (!ok) {
+ throw new SecurityException("sealing violation");
+ }
+ } else {
+ if (man != null) {
+ definePackage(pkgname, man, url);
+ } else {
+ definePackage(pkgname, null, null, null, null, null, null, null);
+ }
+ }
}
+
+ byte[] data = res.getBytes();
+ clazz = defineClass(name, data, 0, data.length);
+ res.setLoadedClass(clazz);
}
- // No changes, no need to reload
- log.debug("shouldReload: No expired resource found, no need to reload");
- return false;
+ return clazz;
}
/**
+ * Returns true if the specified package name is sealed according to the
+ * given manifest
+ * <p>
+ * This code is duplicate from <code>URLClassLoader.isSealed</code> because
+ * the latter has private access and we need the method here.
+ */
+ private boolean isSealed(String name, Manifest man) {
+ String path = name.replace('.', '/').concat("/");
+ Attributes attr = man.getAttributes(path);
+ String sealed = null;
+ if (attr != null) {
+ sealed = attr.getValue(Attributes.Name.SEALED);
+ }
+ if (sealed == null) {
+ if ((attr = man.getMainAttributes()) != null) {
+ sealed = attr.getValue(Attributes.Name.SEALED);
+ }
+ }
+ return "true".equalsIgnoreCase(sealed);
+ }
+
+ //---------- reload support ------------------------------------------------
+
+ /**
* Returns whether the class loader is dirty. This can be the case if any
- * of the {@link #shouldReload(String)} or {@link #shouldReload()}
- * methods returned <code>true</code> or if a loaded class has been expired
- * through the observation.
+ * of the loaded class has been expired through the observation.
* <p>
* This method may also return <code>true</code> if the <code>Session</code>
* associated with this class loader is not valid anymore.
@@ -309,7 +705,7 @@ public class DynamicRepositoryClassLoader
* reinstantiation.
*/
public boolean isDirty() {
- return isDestroyed() || dirty || !getSession().isLive();
+ return destroyed || dirty || !session.isLive();
}
/**
@@ -332,7 +728,7 @@ public class DynamicRepositoryClassLoader
public DynamicRepositoryClassLoader reinstantiate(Session session, ClassLoader parent) {
log.debug("reinstantiate: Copying {} with parent {}", this, parent);
- if (isDestroyed()) {
+ if (destroyed) {
throw new IllegalStateException("Destroyed class loader cannot be recreated");
}
@@ -344,115 +740,7 @@ public class DynamicRepositoryClassLoader
return newLoader;
}
- //---------- URLClassLoader overwrites -------------------------------------
-
- /**
- * Reconfigures this class loader with the pattern list. That is the new
- * pattern list completely replaces the current pattern list. This new
- * pattern list will also be used later to configure the reinstantiated
- * class loader.
- * <p>
- * If this class loader already has loaded classes using the old, replaced
- * path list, it is set dirty.
- * <p>
- * If this class loader has already been destroyed, this method has no
- * effect.
- *
- * @param classPath The list of path strings making up the (initial) class
- * path of this class loader. The strings may contain globbing
- * characters which will be resolved to build the actual class path.
- */
- public void reconfigure(String[] classPath) {
- if (log.isDebugEnabled()) {
- log.debug("reconfigure: Reconfiguring the with {}",
- Arrays.asList(classPath));
- }
-
- // whether the loader is destroyed
- if (isDestroyed()) {
- log.warn("Cannot reconfigure this destroyed class loader");
- return;
- }
-
- // assign new path and register
- setPaths(classPath);
- buildRepository();
-
- dirty = !hasLoadedResources();
- log.debug("reconfigure: Class loader is dirty now: {}", (isDirty()
- ? "yes"
- : "no"));
- }
-
- //---------- RepositoryClassLoader overwrites -----------------------------
-
- /**
- * Calls the base class implementation to actually retrieve the resource.
- * If the resource could be found and provides a non-<code>null</code>
- * {@link ClassLoaderResource#getExpiryProperty() expiry property}, the
- * resource is registered with an internal cache to check with when
- * a repository modification is observed in {@link #onEvent(EventIterator)}.
- *
- * @param name The name of the resource to be found
- *
- * @return the {@link ClassLoaderResource} found for the name or
- * <code>null</code> if no such resource is available in the class
- * path.
- *
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- /* package */ ClassLoaderResource findClassLoaderResource(String name) {
- // call the base class implementation to actually search for it
- ClassLoaderResource res = super.findClassLoaderResource(name);
-
- // if it could be found, we register it with the caches
- if (res != null) {
- // register the resource in the expiry map, if an appropriate
- // property is available
- Property prop = res.getExpiryProperty();
- if (prop != null) {
- try {
- modTimeCache.put(prop.getPath(), res);
- } catch (RepositoryException re) {
- log.warn("Cannot register the resource " + res +
- " for expiry", re);
- }
- }
- }
-
- // and finally return the resource
- return res;
- }
-
- /**
- * Builds the repository list from the list of path patterns and appends
- * the path entries from any added handles. This method may be used multiple
- * times, each time replacing the currently defined repository list.
- *
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- protected synchronized void buildRepository() {
- super.buildRepository();
-
- // add added repositories
- ClassPathEntry[] addedPath = getAddedRepositories();
- if (addedPath != null && addedPath.length > 0) {
- ClassPathEntry[] oldClassPath = getRepository();
- ClassPathEntry[] newClassPath =
- new ClassPathEntry[oldClassPath.length + addedPath.length];
-
- System.arraycopy(oldClassPath, 0, newClassPath, 0,
- oldClassPath.length);
- System.arraycopy(addedPath, 0, newClassPath, oldClassPath.length,
- addedPath.length);
-
- setRepository(newClassPath);
- }
- }
-
- //---------- ModificationListener interface -------------------------------
+ //---------- EventListener interface -------------------------------
/**
* Handles a repository item modifcation events checking whether a class
@@ -497,58 +785,23 @@ public class DynamicRepositoryClassLoader
* Returns a string representation of this class loader.
*/
public String toString() {
- if (isDestroyed()) {
- return super.toString();
+ StringBuilder buf = new StringBuilder(getClass().getName());
+ if (destroyed) {
+ buf.append(" - destroyed");
+ } else {
+ buf.append(": parent: { ");
+ buf.append(getParent());
+ buf.append(" }, user: ");
+ buf.append(session.getUserID());
+ buf.append(", dirty: ");
+ buf.append(isDirty());
}
-
- StringBuilder buf = new StringBuilder(super.toString());
- buf.append(", dirty: ");
- buf.append(isDirty());
return buf.toString();
}
//---------- internal ------------------------------------------------------
/**
- * Sets the list of class path entries to add to the class path after
- * reconfiguration or reinstantiation.
- *
- * @param addedRepositories The list of class path entries to keep for
- * readdition.
- */
- protected void setAddedRepositories(ClassPathEntry[] addedRepositories) {
- this.addedRepositories = addedRepositories;
- }
-
- /**
- * Returns the list of added class path entries to readd them to the class
- * path after reconfiguring the class loader.
- */
- protected ClassPathEntry[] getAddedRepositories() {
- return addedRepositories;
- }
-
- /**
- * Adds the class path entry to the current class path list. If the class
- * loader has already been destroyed, this method creates a single entry
- * class path list with the new class path entry.
- * <p>
- * Besides adding the entry to the current class path, it is also added to
- * the list to be readded after reconfiguration and/or reinstantiation.
- *
- * @see #getAddedRepositories()
- * @see #setAddedRepositories(ClassPathEntry[])
- */
- protected void addClassPathEntry(ClassPathEntry cpe) {
- super.addClassPathEntry(cpe);
-
- // add the repsitory to the list of added repositories
- ClassPathEntry[] oldClassPath = getAddedRepositories();
- ClassPathEntry[] newClassPath = addClassPathEntry(oldClassPath, cpe);
- setAddedRepositories(newClassPath);
- }
-
- /**
* Registers this class loader with the observation service to get
* information on page updates in the class path and to the path
* pattern list to get class path updates.
@@ -556,16 +809,15 @@ public class DynamicRepositoryClassLoader
* @throws NullPointerException if this class loader has already been
* destroyed.
*/
- private final void registerModificationListener() {
- log.debug("registerModificationListener: Registering to the observation service");
+ private final void registerListeners() {
+ log.debug("registerListeners: Registering to the observation service");
- final String[] paths = this.getPaths();
- this.proxyListeners = new EventListener[this.getPaths().length];
+ this.proxyListeners = new EventListener[this.paths.length];
for(int i=0; i < paths.length; i++ ) {
final String path = paths[i];
try {
final EventListener listener = new ProxyEventListener(this);
- final ObservationManager om = getSession().getWorkspace().getObservationManager();
+ final ObservationManager om = session.getWorkspace().getObservationManager();
om.addEventListener(listener, 255, path, true, null, null, false);
proxyListeners[i] = listener;
} catch (RepositoryException re) {
@@ -582,13 +834,13 @@ public class DynamicRepositoryClassLoader
* @throws NullPointerException if this class loader has already been
* destroyed.
*/
- private final void unregisterListener() {
- log.debug("registerModificationListener: Deregistering from the observation service");
+ private final void unregisterListeners() {
+ log.debug("unregisterListeners: Deregistering from the observation service");
if ( this.proxyListeners != null ) {
for(final EventListener listener : this.proxyListeners) {
if ( listener != null ) {
try {
- final ObservationManager om = getSession().getWorkspace().getObservationManager();
+ final ObservationManager om = session.getWorkspace().getObservationManager();
om.removeEventListener(listener);
} catch (RepositoryException re) {
log.error("unregisterListener: Cannot unregister " +
@@ -622,27 +874,6 @@ public class DynamicRepositoryClassLoader
return exp;
}
- /**
- * Returns the list of classpath entries after resetting each of them.
- *
- * @param list The list of {@link ClassPathEntry}s to reset
- *
- * @return The list of reset {@link ClassPathEntry}s.
- */
- private ClassPathEntry[] resetClassPathEntries(
- ClassPathEntry[] oldClassPath) {
- if (oldClassPath != null) {
- for (int i=0; i < oldClassPath.length; i++) {
- ClassPathEntry entry = oldClassPath[i];
- log.debug("resetClassPathEntries: Cloning {}", entry);
- oldClassPath[i] = entry.copy();
- }
- } else {
- log.debug("resetClassPathEntries: No list to reset");
- }
- return oldClassPath;
- }
-
protected final static class ProxyEventListener implements EventListener {
private final EventListener delegatee;
diff --git a/src/main/java/org/apache/sling/jcr/classloader/internal/URLRepositoryClassLoader.java b/src/main/java/org/apache/sling/jcr/classloader/internal/URLRepositoryClassLoader.java
deleted file mode 100644
index fddfcb9..0000000
--- a/src/main/java/org/apache/sling/jcr/classloader/internal/URLRepositoryClassLoader.java
+++ /dev/null
@@ -1,794 +0,0 @@
-/*
- * 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.beans.Introspector;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.jar.Attributes;
-import java.util.jar.Manifest;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.sling.jcr.classloader.internal.net.JCRURLConnection;
-import org.apache.sling.jcr.classloader.internal.net.URLFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * The <code>RepositoryClassLoader</code> class extends the
- * <code>URLClassLoader</code> and provides the functionality to load classes
- * and resources from JCR Repository.
- * <p>
- * This class loader supports loading classes from the Repository hierarchy,
- * such as a <em>classes</em> 'folder', but also from Jar and Zip files stored
- * in the Repository.
- * <p>
- * For enhanced performance, this class loader keeps a list of resources and
- * classes which have already been loaded through this class loader. If later
- * requests ask for already cached resources, these are returned without
- * checking whether the underlying repository actually still exists.
- * <p>
- */
-public class URLRepositoryClassLoader extends URLClassLoader {
-
- /** default log category */
- private final Logger log =
- LoggerFactory.getLogger(this.getClass().getName());
-
- /** An empty list of url paths to call superclass constructor */
- private static final URL[] NULL_PATH = {};
-
- /**
- * The special resource representing a resource which could not be
- * found in the class path.
- *
- * @see #cache
- * @see #findClassLoaderResource(String)
- */
- /* package */ static final ClassLoaderResource NOT_FOUND_RESOURCE =
- new ClassLoaderResource(null, "[sentinel]", null) {
- public boolean isExpired() {
- return false;
- }
- };
-
- /**
- * The classpath which this classloader searches for class definitions.
- * Each element of the vector should be either a directory, a .zip
- * file, or a .jar file.
- * <p>
- * It may be empty when only system classes are controlled.
- */
- private ClassPathEntry[] repository;
-
- /**
- * The list of paths to use as a classpath.
- */
- private String[] paths;
-
- /**
- * The <code>Session</code> grants access to the Repository to access the
- * resources.
- * <p>
- * This field is not final such that it may be cleared when the class loader
- * is destroyed.
- */
- private Session session;
-
- /**
- * Cache of resources found or not found in the class path. The map is
- * indexed by resource name and contains mappings to instances of the
- * {@link ClassLoaderResource} class. If a resource has been tried to be
- * loaded, which could not be found, the resource is cached with the
- * special mapping to {@link #NOT_FOUND_RESOURCE}.
- *
- * @see #NOT_FOUND_RESOURCE
- * @see #findClassLoaderResource(String)
- */
- private Map<String, ClassLoaderResource> cache;
-
- /**
- * Flag indicating whether the {@link #destroy()} method has already been
- * called (<code>true</code>) or not (<code>false</code>)
- */
- private boolean destroyed;
-
- /**
- * Creates a <code>RepositoryClassLoader</code> from a list of item path
- * strings containing globbing pattens for the paths defining the class
- * path.
- *
- * @param session The <code>Session</code> to use to access the class items.
- * @param classPath The list of path strings making up the (initial) class
- * path of this class loader. The strings may contain globbing
- * characters which will be resolved to build the actual class path.
- * @param parent The parent <code>ClassLoader</code>, which may be
- * <code>null</code>.
- *
- * @throws NullPointerException if either the session or the handles list is
- * <code>null</code>.
- */
- public URLRepositoryClassLoader(Session session, String[] classPath,
- ClassLoader parent) {
- // initialize the super class with an empty class path
- super(NULL_PATH, parent);
-
- // check session and handles
- if (session == null) {
- throw new NullPointerException("session");
- }
- if (classPath == null || classPath.length == 0) {
- throw new NullPointerException("handles");
- }
-
- // set fields
- this.session = session;
- setPaths(classPath);
- this.cache = new HashMap<String, ClassLoaderResource>();
- this.destroyed = false;
-
- // build the class repositories list
- buildRepository();
-
- log.debug("RepositoryClassLoader: {} ready", this);
- }
-
- /**
- * Returns <code>true</code> if this class loader has already been destroyed
- * by calling {@link #destroy()}.
- */
- protected boolean isDestroyed() {
- return destroyed;
- }
-
- protected String[] getPaths() {
- return this.paths;
- }
-
- protected void setPaths(final String[] classPath) {
- this.paths = classPath;
- }
-
- /**
- * Destroys this class loader. This process encompasses all steps needed
- * to remove as much references to this class loader as possible.
- * <p>
- * <em>NOTE</em>: This method just clears all internal fields and especially
- * the class path to render this class loader unusable.
- * <p>
- * This implementation does not throw any exceptions.
- */
- public void destroy() {
- // we expect to be called only once, so we stop destroyal here
- if (isDestroyed()) {
- log.debug("Instance is already destroyed");
- return;
- }
-
- // set destroyal guard
- destroyed = true;
-
- // clear caches and references
- setRepository(null);
- setPaths(null);
- session = null;
-
- // clear the cache of loaded resources and flush cached class
- // introspections of the JavaBean framework
- if (cache != null) {
- final Iterator<ClassLoaderResource> ci = cache.values().iterator();
- while ( ci.hasNext() ) {
- final ClassLoaderResource res = ci.next();
- if (res.getLoadedClass() != null) {
- Introspector.flushFromCaches(res.getLoadedClass());
- res.setLoadedClass(null);
- }
- }
- cache.clear();
- }
- }
-
- //---------- URLClassLoader overwrites -------------------------------------
-
- /**
- * Finds and loads the class with the specified name from the class path.
- *
- * @param name the name of the class
- * @return the resulting class
- *
- * @throws ClassNotFoundException If the named class could not be found or
- * if this class loader has already been destroyed.
- */
- protected Class<?> findClass(final String name) throws ClassNotFoundException {
-
- if (isDestroyed()) {
- throw new ClassNotFoundException(name + " (Classloader destroyed)");
- }
-
- log.debug("findClass: Try to find class {}", name);
-
- try {
- return AccessController.doPrivileged(
- new PrivilegedExceptionAction<Class<?>>() {
-
- public Class<?> run() throws ClassNotFoundException {
- return findClassPrivileged(name);
- }
- });
- } catch (java.security.PrivilegedActionException pae) {
- throw (ClassNotFoundException) pae.getException();
- }
- }
-
- /**
- * Finds the resource with the specified name on the search path.
- *
- * @param name the name of the resource
- *
- * @return a <code>URL</code> for the resource, or <code>null</code>
- * if the resource could not be found or if the class loader has
- * already been destroyed.
- */
- public URL findResource(String name) {
-
- if (isDestroyed()) {
- log.warn("Destroyed class loader cannot find a resource");
- return null;
- }
-
- log.debug("findResource: Try to find resource {}", name);
-
- ClassLoaderResource res = findClassLoaderResource(name);
- if (res != null) {
- log.debug("findResource: Getting resource from {}, created {}",
- res, new Date(res.getLastModificationTime()));
- return res.getURL();
- }
-
- return null;
- }
-
- /**
- * Returns an Enumeration of URLs representing all of the resources
- * on the search path having the specified name.
- *
- * @param name the resource name
- *
- * @return an <code>Enumeration</code> of <code>URL</code>s. This is an
- * empty enumeration if no resources are found by this class loader
- * or if this class loader has already been destroyed.
- */
- public Enumeration<URL> findResources(String name) {
-
- if (isDestroyed()) {
- log.warn("Destroyed class loader cannot find resources");
- return new Enumeration<URL>() {
- public boolean hasMoreElements() {
- return false;
- }
- public URL nextElement() {
- throw new NoSuchElementException("No Entries");
- }
- };
- }
-
- log.debug("findResources: Try to find resources for {}", name);
-
- List<URL> list = new LinkedList<URL>();
- for (int i=0; i < repository.length; i++) {
- final ClassPathEntry cp = repository[i];
- log.debug("findResources: Trying {}", cp);
-
- ClassLoaderResource res = cp.getResource(name);
- if (res != null) {
- log.debug("findResources: Adding resource from {}, created {}",
- res, new Date(res.getLastModificationTime()));
- URL url = res.getURL();
- if (url != null) {
- list.add(url);
- }
- }
-
- }
-
- // return the enumeration on the list
- return Collections.enumeration(list);
- }
-
- /**
- * Returns the search path of URLs for loading classes and resources.
- * This includes the original list of URLs specified to the constructor,
- * along with any URLs subsequently appended by the {@link #addURL(URL)}.
- *
- * @return the search path of URLs for loading classes and resources. The
- * list is empty, if this class loader has already been destroyed.
- * @see java.net.URLClassLoader#getURLs()
- */
- public URL[] getURLs() {
- if (isDestroyed()) {
- log.warn("Destroyed class loader has no URLs any more");
- return new URL[0];
- }
-
- List<URL> urls = new ArrayList<URL>();
- for (int i=0; i < repository.length; i++) {
- URL url = repository[i].toURL();
- if (url != null) {
- urls.add(url);
- }
- }
- return urls.toArray(new URL[urls.size()]);
- }
-
- /**
- * Appends the specified URL to the list of URLs to search for
- * classes and resources. Only Repository URLs with the protocol set to
- * <code>JCR</code> are considered for addition. The system will find out
- * whether the URL points to a directory or a jar archive.
- * <p>
- * URLs added using this method will be preserved through reconfiguration
- * and reinstantiation.
- * <p>
- * If this class loader has already been destroyed this method has no
- * effect.
- *
- * @param url the <code>JCR</code> URL to be added to the search path of
- * URLs.
- * @see java.net.URLClassLoader#addURL(java.net.URL)
- */
- protected void addURL(URL url) {
- if (isDestroyed()) {
- log.warn("Cannot add URL to destroyed class loader");
-
- } else if (checkURL(url)) {
- // Repository URL
- log.debug("addURL: Adding URL {}", url);
- try {
- JCRURLConnection conn = (JCRURLConnection) url.openConnection();
- ClassPathEntry cp = ClassPathEntry.getInstance(
- conn.getSession(), conn.getPath());
- addClassPathEntry(cp);
- } catch (IOException ioe) {
- log.warn("addURL: Cannot add URL " + url, ioe);
- }
-
- } else {
- log.warn("addURL: {} is not a Repository URL, ignored", url);
- }
- }
-
- //---------- Property access ----------------------------------------------
-
- /**
- * Returns the named {@link ClassLoaderResource} if it is contained in the
- * cache. If the resource does not exist in the cache or has not been found
- * in the class path at an earlier point in time, <code>null</code> is
- * returned.
- *
- * @param name The name of the resource to retrieve from the cache.
- *
- * @return The named <code>ClassLoaderResource</code> or <code>null</code>
- * if not loaded.
- *
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- /* package */ ClassLoaderResource getCachedResource(String name) {
- Object res = cache.get(name);
- if (res == null || res == NOT_FOUND_RESOURCE) {
- log.debug("Resource {} not cached", name);
- return null;
- }
-
- return (ClassLoaderResource) res;
- }
-
- /**
- * Returns an <code>Iterator</code> on all resources in the cache. This
- * iterator may also contain {@link #NOT_FOUND_RESOURCE sentinel} entries
- * for resources, which failed to load. Callers of this method should take
- * care to filter out such resources before acting on it.
- *
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- /* package */ Iterator<ClassLoaderResource> getCachedResources() {
- return cache.values().iterator();
- }
-
- /**
- * Removes all entries from the cache of loaded resources, which mark
- * resources, which have not been found as of yet.
- *
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- protected void cleanCache() {
- final Iterator<ClassLoaderResource> ci = this.cache.values().iterator();
- while (ci.hasNext()) {
- if (ci.next() == NOT_FOUND_RESOURCE) {
- ci.remove();
- }
- }
- }
-
- /**
- * Returns <code>true</code>, if the cache is not empty. If the
- * {@link #cleanCache()} method is not called before calling this method, a
- * false positive result may be returned.
- *
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- protected boolean hasLoadedResources() {
- return cache.isEmpty();
- }
-
- /**
- * Returns the session used by this class loader to access the repository.
- * If this class loader has already been destroyed, this <code>null</code>
- * is returned.
- */
- protected Session getSession() {
- return session;
- }
-
- /**
- * Sets the current active class path to the list of class path entries.
- */
- protected void setRepository(ClassPathEntry[] classPath) {
- this.repository = classPath;
- }
-
- /**
- * Returns the current active class path entries list or <code>null</code>
- * if this class loader has already been destroyed.
- */
- protected ClassPathEntry[] getRepository() {
- return repository;
- }
-
- /**
- * Adds the class path entry to the current class path list. If the class
- * loader has already been destroyed, this method creates a single entry
- * class path list with the new class path entry.
- */
- protected void addClassPathEntry(ClassPathEntry cpe) {
- log.debug("addHandle: Adding path {}", cpe.getPath());
-
- // append the entry to the current class path
- ClassPathEntry[] oldClassPath = getRepository();
- ClassPathEntry[] newClassPath = addClassPathEntry(oldClassPath, cpe);
- setRepository(newClassPath);
- }
-
- /**
- * Helper method for class path handling to a new entry to an existing
- * list and return the new list.
- * <p>
- * If <code>list</code> is <code>null</code> a new array is returned with
- * a single element <code>newEntry</code>. Otherwise the array returned
- * contains all elements of <code>list</code> and <code>newEntry</code>
- * at the last position.
- *
- * @param list The array of class path entries, to which a new entry is
- * to be appended. This may be <code>null</code>.
- * @param newEntry The new entry to append to the class path list.
- *
- * @return The extended class path list.
- */
- protected ClassPathEntry[] addClassPathEntry(ClassPathEntry[] list,
- ClassPathEntry newEntry) {
-
- // quickly define single entry array for the first entry
- if (list == null) {
- return new ClassPathEntry[]{ newEntry };
- }
-
- // create new array and copy old and new contents
- ClassPathEntry[] newList = new ClassPathEntry[list.length+1];
- System.arraycopy(list, 0, newList, 0, list.length);
- newList[list.length] = newEntry;
- return newList;
- }
-
- //---------- Object overwrite ---------------------------------------------
-
- /**
- * Returns a string representation of this instance.
- */
- public String toString() {
- StringBuilder buf = new StringBuilder(getClass().getName());
-
- if (isDestroyed()) {
- buf.append(" - destroyed");
- } else {
- buf.append(": parent: { ");
- buf.append(getParent());
- buf.append(" }, user: ");
- buf.append(session.getUserID());
- }
-
- return buf.toString();
- }
-
- //---------- internal ------------------------------------------------------
-
- /**
- * Builds the repository list from the list of path patterns and appends
- * the path entries from any added handles. This method may be used multiple
- * times, each time replacing the currently defined repository list.
- *
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- protected synchronized void buildRepository() {
- List<ClassPathEntry> newRepository = new ArrayList<ClassPathEntry>(paths.length);
-
- // build repository from path patterns
- for (int i=0; i < paths.length; i++) {
- final String entry = paths[i];
- ClassPathEntry cp = null;
-
- // try to find repository based on this path
- if (repository != null) {
- for (int j=0; j < repository.length; j++) {
- final ClassPathEntry tmp = repository[i];
- if (tmp.getPath().equals(entry)) {
- cp = tmp;
- break;
- }
- }
- }
-
- // not found, creating new one
- if (cp == null) {
- cp = ClassPathEntry.getInstance(session, entry);
- }
-
- if (cp != null) {
- log.debug("Adding path {}", entry);
- newRepository.add(cp);
- } else {
- log.debug("Cannot get a ClassPathEntry for {}", entry);
- }
- }
-
- // replace old repository with new one
- final ClassPathEntry[] newClassPath = new ClassPathEntry[newRepository.size()];
- newRepository.toArray(newClassPath);
- setRepository(newClassPath);
-
- // clear un-found resource cache
- cleanCache();
- }
-
- /**
- * Tries to find the class in the class path from within a
- * <code>PrivilegedAction</code>. Throws <code>ClassNotFoundException</code>
- * if no class can be found for the name.
- *
- * @param name the name of the class
- *
- * @return the resulting class
- *
- * @throws ClassNotFoundException if the class could not be found
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- private Class<?> findClassPrivileged(String name) throws ClassNotFoundException {
-
- // prepare the name of the class
- final String path = name.replace('.', '/').concat(".class");
- log.debug("findClassPrivileged: Try to find path {} for class {}",
- path, name);
-
- ClassLoaderResource res = findClassLoaderResource(path);
- if (res != null) {
-
- // try defining the class, error aborts
- try {
- log.debug(
- "findClassPrivileged: Loading class from {}, created {}",
- res, new Date(res.getLastModificationTime()));
-
- Class<?> c = defineClass(name, res);
- if (c == null) {
- log.warn("defineClass returned null for class {}", name);
- throw new ClassNotFoundException(name);
- }
- return c;
-
- } catch (IOException ioe) {
- log.debug("defineClass failed", ioe);
- throw new ClassNotFoundException(name, ioe);
- } catch (Throwable t) {
- log.debug("defineClass failed", t);
- throw new ClassNotFoundException(name, t);
- }
- }
-
- throw new ClassNotFoundException(name);
- }
-
- /**
- * Returns a {@link ClassLoaderResource} for the given <code>name</code> or
- * <code>null</code> if not existing. If the resource has already been
- * loaded earlier, the cached instance is returned. If the resource has
- * not been found in an earlier call to this method, <code>null</code> is
- * returned. Otherwise the resource is looked up in the class path. If
- * found, the resource is cached and returned. If not found, the
- * {@link #NOT_FOUND_RESOURCE} is cached for the name and <code>null</code>
- * is returned.
- *
- * @param name The name of the resource to return.
- *
- * @return The named <code>ClassLoaderResource</code> if found or
- * <code>null</code> if not found.
- *
- * @throws NullPointerException If this class loader has already been
- * destroyed.
- */
- /* package */ ClassLoaderResource findClassLoaderResource(String name) {
-
- // check for cached resources first
- ClassLoaderResource res = cache.get(name);
- if (res == NOT_FOUND_RESOURCE) {
- log.debug("Resource '{}' known to not exist in class path", name);
- return null;
- } else if (res != null) {
- return res;
- }
-
- // walk the repository list and try to find the resource
- for (int i = 0; i < repository.length; i++) {
- final ClassPathEntry cp = repository[i];
- log.debug("Checking {}", cp);
-
- res = cp.getResource(name);
- if (res != null) {
- log.debug("Found resource in {}, created ", res, new Date(
- res.getLastModificationTime()));
- cache.put(name, res);
- return res;
- }
-
- }
-
- log.debug("No classpath entry contains {}", name);
- cache.put(name, NOT_FOUND_RESOURCE);
- return null;
- }
-
- /**
- * Defines a class getting the bytes for the class from the resource
- *
- * @param name The fully qualified class name
- * @param res The resource to obtain the class bytes from
- *
- * @throws RepositoryException If a problem occurrs getting at the data.
- * @throws IOException If a problem occurrs reading the class bytes from
- * the resource.
- * @throws ClassFormatError If the class bytes read from the resource are
- * not a valid class.
- */
- private Class<?> defineClass(String name, ClassLoaderResource res)
- throws IOException, RepositoryException {
-
- log.debug("defineClass({}, {})", name, res);
-
- Class<?> clazz = res.getLoadedClass();
- if (clazz == null) {
-
- /**
- * This following code for packages is duplicate from URLClassLoader
- * because it is private there. I would like to not be forced to
- * do this, but I still have to find a way ... -fmeschbe
- */
-
- // package support
- int i = name.lastIndexOf('.');
- if (i != -1) {
- String pkgname = name.substring(0, i);
- // Check if package already loaded.
- Package pkg = getPackage(pkgname);
- URL url = res.getCodeSourceURL();
- Manifest man = res.getManifest();
- if (pkg != null) {
- // Package found, so check package sealing.
- boolean ok;
- if (pkg.isSealed()) {
- // Verify that code source URL is the same.
- ok = pkg.isSealed(url);
- } else {
- // Make sure we are not attempting to seal the package
- // at this code source URL.
- ok = (man == null) || !isSealed(pkgname, man);
- }
- if (!ok) {
- throw new SecurityException("sealing violation");
- }
- } else {
- if (man != null) {
- definePackage(pkgname, man, url);
- } else {
- definePackage(pkgname, null, null, null, null, null, null, null);
- }
- }
- }
-
- byte[] data = res.getBytes();
- clazz = defineClass(name, data, 0, data.length);
- res.setLoadedClass(clazz);
- }
-
- return clazz;
- }
-
- /**
- * Returns true if the specified package name is sealed according to the
- * given manifest
- * <p>
- * This code is duplicate from <code>URLClassLoader.isSealed</code> because
- * the latter has private access and we need the method here.
- */
- private boolean isSealed(String name, Manifest man) {
- String path = name.replace('.', '/').concat("/");
- Attributes attr = man.getAttributes(path);
- String sealed = null;
- if (attr != null) {
- sealed = attr.getValue(Attributes.Name.SEALED);
- }
- if (sealed == null) {
- if ((attr = man.getMainAttributes()) != null) {
- sealed = attr.getValue(Attributes.Name.SEALED);
- }
- }
- return "true".equalsIgnoreCase(sealed);
- }
-
- /**
- * Returns <code>true</code> if the <code>url</code> is a <code>JCR</code>
- * URL.
- *
- * @param url The URL to check whether it is a valid <code>JCR</code> URL.
- *
- * @return <code>true</code> if <code>url</code> is a valid <code>JCR</code>
- * URL.
- *
- * @throws NullPointerException if <code>url</code> is <code>null</code>.
- */
- private boolean checkURL(URL url) {
- return URLFactory.REPOSITORY_SCHEME.equalsIgnoreCase(url.getProtocol());
- }
-}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.