SLING-2447 : ClassLoaderWriter should provide class loader for loading written classes/resources

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1305479 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/jcr/classloader/internal/ClassLoaderWriterImpl.java b/src/main/java/org/apache/sling/jcr/classloader/internal/ClassLoaderWriterImpl.java
index 35485e4..10776f2 100644
--- a/src/main/java/org/apache/sling/jcr/classloader/internal/ClassLoaderWriterImpl.java
+++ b/src/main/java/org/apache/sling/jcr/classloader/internal/ClassLoaderWriterImpl.java
@@ -136,7 +136,7 @@
     /**
      * Return a new session.
      */
-    private Session getSession() throws RepositoryException {
+    public Session getSession() throws RepositoryException {
         // get an administrative session for potentiall impersonation
         final Session admin = this.repository.loginAdministrative(null);
 
@@ -153,19 +153,22 @@
         }
     }
 
+    /**
+     * Is this still active?
+     */
+    public boolean isActivate() {
+        return this.repository != null;
+    }
+
     private synchronized ClassLoader getOrCreateClassLoader() {
         if ( this.repositoryClassLoader == null || !this.repositoryClassLoader.isLive() ) {
             if ( this.repositoryClassLoader != null ) {
                 this.repositoryClassLoader.destroy();
             }
-            try {
-                this.repositoryClassLoader = new RepositoryClassLoader(
-                        this.getSession(),
-                        this.classPath,
-                        this.dynamicClassLoaderManager.getDynamicClassLoader());
-            } catch ( final RepositoryException re) {
-                throw new RuntimeException("Unable to instantiate repository class loader.", re);
-            }
+            this.repositoryClassLoader = new RepositoryClassLoader(
+                    this.classPath,
+                    this,
+                    this.dynamicClassLoaderManager.getDynamicClassLoader());
         }
         return this.repositoryClassLoader;
     }
diff --git a/src/main/java/org/apache/sling/jcr/classloader/internal/RepositoryClassLoader.java b/src/main/java/org/apache/sling/jcr/classloader/internal/RepositoryClassLoader.java
index 2eb5125..abf0690 100644
--- a/src/main/java/org/apache/sling/jcr/classloader/internal/RepositoryClassLoader.java
+++ b/src/main/java/org/apache/sling/jcr/classloader/internal/RepositoryClassLoader.java
@@ -69,13 +69,12 @@
     private String repositoryPath;
 
     /**
-     * The <code>Session</code> grants access to the Repository to access the
-     * resources.
+     * The <code>ClassLoaderWriterImpl</code> grants access to the repository
      * <p>
      * This field is not final such that it may be cleared when the class loader
      * is destroyed.
      */
-    private Session session;
+    private ClassLoaderWriterImpl writer;
 
     /**
      * Flag indicating whether the {@link #destroy()} method has already been
@@ -84,36 +83,39 @@
     private boolean destroyed = false;
 
     /**
-     * Creates a <code>DynamicRepositoryClassLoader</code> from a list of item
-     * path strings containing globbing pattens for the paths defining the
-     * class path.
+     * Session to serve urls.
+     */
+    private Session urlHandlerSession;
+
+    /**
+     * Creates a <code>RepositoryClassLoader</code> for a given
+     * repository 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 classPath The path making up the class path of this class
+     *                  loder
+     * @param writer The class loader write to get a jcr session.
      * @param parent The parent <code>ClassLoader</code>, which may be
      *      <code>null</code>.
      *
      * @throws NullPointerException if either the session or the classPath
      *      is <code>null</code>.
      */
-    public RepositoryClassLoader(final Session session,
-                                 final String classPath,
+    public RepositoryClassLoader(final String classPath,
+                                 final ClassLoaderWriterImpl writer,
                                  final ClassLoader parent) {
         // initialize the super class with an empty class path
         super(parent);
 
-        // check session and handles
-        if (session == null) {
-            throw new NullPointerException("session");
+        // check writer and classPath
+        if (writer == null) {
+            throw new NullPointerException("writer");
         }
         if (classPath == null) {
             throw new NullPointerException("classPath");
         }
 
         // set fields
-        this.session = session;
+        this.writer = writer;
         this.repositoryPath = classPath;
 
         logger.debug("RepositoryClassLoader: {} ready", this);
@@ -138,12 +140,13 @@
         // set destroyal guard
         destroyed = true;
 
-        // close session
-        if ( session != null ) {
-            session.logout();
-            session = null;
+        if ( this.urlHandlerSession != null ) {
+            this.urlHandlerSession.logout();
+            this.urlHandlerSession = null;
         }
-        repositoryPath = null;
+
+        this.writer = null;
+        this.repositoryPath = null;
         synchronized ( this.usedResources ) {
             this.usedResources.clear();
         }
@@ -196,20 +199,33 @@
         logger.debug("findResource: Try to find resource {}", name);
 
         final String path = this.repositoryPath + '/' + name;
-        final Node res = findClassLoaderResource(path);
-        if (res != null) {
-            logger.debug("findResource: Getting resource from {}",
-                res);
-            try {
-                return URLFactory.createURL(session, res.getPath());
-            } catch (Exception e) {
-                logger.warn("findResource: Cannot getURL for " + name, e);
+        try {
+            if ( findClassLoaderResource(path) != null ) {
+                logger.debug("findResource: Getting resource from {}", path);
+                return URLFactory.createURL(this.getUrlHandlerSession(), path);
             }
+        } catch (final Exception e) {
+            logger.warn("findResource: Cannot getURL for " + name, e);
         }
 
         return null;
     }
 
+    private synchronized Session getUrlHandlerSession() {
+        if ( this.urlHandlerSession != null && !this.urlHandlerSession.isLive() ) {
+            this.urlHandlerSession.logout();
+            this.urlHandlerSession = null;
+        }
+        if ( this.urlHandlerSession == null ) {
+            try {
+                this.urlHandlerSession = this.writer.getSession();
+            } catch ( final RepositoryException re ) {
+                logger.warn("Unable to create new session.", re);
+            }
+        }
+        return this.urlHandlerSession;
+    }
+
     /**
      * Returns an Enumeration of URLs representing all of the resources
      * on the search path having the specified name.
@@ -265,63 +281,63 @@
             name);
 
         final String path = this.repositoryPath + '/' + name.replace('.', '/') + (".class");
-        final Node res = this.findClassLoaderResource(path);
-        if (res != null) {
 
-             // try defining the class, error aborts
-             try {
+         // try defining the class, error aborts
+         try {
+             final byte[] data = this.findClassLoaderResource(path);
+             if (data != null) {
+
                  logger.debug(
-                    "findClassPrivileged: Loading class from {}", res);
+                "findClassPrivileged: Loading class from {}", data);
 
-                 final Class<?> c = defineClass(name, res);
+                 final Class<?> c = defineClass(name, data);
                  if (c == null) {
                      logger.warn("defineClass returned null for class {}", name);
                      throw new ClassNotFoundException(name);
                  }
                  return c;
-
-             } catch (final IOException ioe) {
-                 logger.debug("defineClass failed", ioe);
-                 throw new ClassNotFoundException(name, ioe);
-             } catch (final Throwable t) {
-                 logger.debug("defineClass failed", t);
-                 throw new ClassNotFoundException(name, t);
              }
+
+         } catch (final IOException ioe) {
+             logger.debug("defineClass failed", ioe);
+             throw new ClassNotFoundException(name, ioe);
+         } catch (final Throwable t) {
+             logger.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.
+     * Returns the contents for the given <code>path</code> or
+     * <code>null</code> if not existing.
      *
-     * @param name The name of the resource to return.
+     * @param path The repository path of the resource to return.
      *
-     * @return The named <code>ClassLoaderResource</code> if found or
-     *      <code>null</code> if not found.
+     * @return The contents if found or <code>null</code> if not found.
      *
      * @throws NullPointerException If this class loader has already been
      *      destroyed.
      */
-    private Node findClassLoaderResource(final String path) {
-        Node res = null;
+    private byte[] findClassLoaderResource(final String path) throws IOException {
+        Session session = null;
+        byte[] res = null;
         try {
+            session = this.writer.getSession();
             if ( session.itemExists(path) ) {
                 final Node node = (Node)session.getItem(path);
                 logger.debug("Found resource at {}", path);
-                res = node;
+                res = Util.getBytes(node);
             } else {
                 logger.debug("No classpath entry contains {}", path);
             }
         } catch (final RepositoryException re) {
             logger.debug("Error while trying to get node at " + path, re);
+        } finally {
+            if ( session != null ) {
+                session.logout();
+            }
         }
         synchronized ( this.usedResources ) {
             this.usedResources.add(path);
@@ -330,10 +346,10 @@
     }
 
     /**
-     * Defines a class getting the bytes for the class from the resource
+     * Defines a class using the bytes
      *
      * @param name The fully qualified class name
-     * @param res The resource to obtain the class bytes from
+     * @param contents The class in bytes
      *
      * @throws RepositoryException If a problem occurrs getting at the data.
      * @throws IOException If a problem occurrs reading the class bytes from
@@ -341,39 +357,19 @@
      * @throws ClassFormatError If the class bytes read from the resource are
      *      not a valid class.
      */
-    private Class<?> defineClass(final String name, final Node res)
-    throws IOException, RepositoryException {
-        logger.debug("defineClass({}, {})", name, res);
+    private Class<?> defineClass(final String name, final byte[] contents) {
+        logger.debug("defineClass({}, {})", name, contents.length);
 
-        final byte[] data = Util.getBytes(res);
-        final Class<?> clazz = defineClass(name, data, 0, data.length);
+        final Class<?> clazz = defineClass(name, contents, 0, contents.length);
 
         return clazz;
     }
 
     /**
-     * Returns whether the class loader is dirty. This can be the case if any
-     * 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.
-     * <p>
-     * Finally the method always returns <code>true</code> if the class loader
-     * has already been destroyed.
-     * <p>
-     *
-     * @return <code>true</code> if the class loader is dirty and needs
-     *      recreation.
-     */
-    public boolean isDirty() {
-        return destroyed || dirty || !session.isLive();
-    }
-
-    /**
      * @see org.apache.sling.commons.classloader.DynamicClassLoader#isLive()
      */
     public boolean isLive() {
-        return !this.isDirty();
+        return !destroyed && !dirty && this.writer != null && this.writer.isActivate();
     }
 
     /**
@@ -400,10 +396,8 @@
         } else {
             buf.append(": parent: { ");
             buf.append(getParent());
-            buf.append(" }, user: ");
-            buf.append(session.getUserID());
-            buf.append(", dirty: ");
-            buf.append(isDirty());
+            buf.append(" }, live: ");
+            buf.append(isLive());
         }
         return buf.toString();
     }