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:21:42 UTC

[sling-org-apache-sling-commons-fsclassloader] annotated tag org.apache.sling.commons.fsclassloader-1.0.6 created (now 449cbb3)

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a change to annotated tag org.apache.sling.commons.fsclassloader-1.0.6
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-fsclassloader.git.


      at 449cbb3  (tag)
 tagging b67f5c7e28fc5f2db6418585700199623a870cb1 (commit)
      by Dan Klco
      on Fri Feb 17 20:53:18 2017 +0000

- Log -----------------------------------------------------------------
org.apache.sling.commons.fsclassloader-1.0.6
-----------------------------------------------------------------------

This annotated tag includes the following new commits:

     new 6cc7225  SLING-4708 -  Move the File System Classloader from contrib to bundles
     new 9de33a2  svn:ignore
     new 1339f45  Working on SLING-4792 : Adding an OSGi Console for viewing the FSClassloader files
     new 7643a3a  SLING-4809 : Filesystem classwriter does not delete directories
     new c501009  [maven-release-plugin] prepare release org.apache.sling.commons.fsclassloader-1.0.2
     new 7393649  [maven-release-plugin] prepare for next development iteration
     new 7a944a8  Update to Sling Parent 23
     new 7e6ec86  SLING-4833 - Clarify licensing of prettify.js and prettify.css
     new d11f1ab  set parent version to 24 and add empty relativePath where missing
     new ccaa55a  Update the main reactor to parent 25
     new f27ac06  SLING-5398 - WebConsole plugin for FS ClassLoader can't load files on windows
     new 9c71aca  Switch to parent pom 26
     new 9def75a  SLING-5573 - The File System Classloader Console Plugin should wrap long lines of code
     new a2fcf9d  trivial: changed tabs to spaces
     new 44a13d8  SLING-4833 - Clarify licensing of prettify.js and prettify.css
     new 75cebad  SLING-5603 Upgrade Commons Lang to 3.4
     new 1b76bbf  SLING-5685 Upgrade Commons IO to 2.5
     new 37162e1  SLING-5601 - The File System Classloader Console Plugin should allow wiping the classloader's cache
     new 2535735  Fix javadocs
     new ebc295a  Update to parent pom 27
     new de1cf93  switch parent pom reference everywhere to 28 where it was 27 before this can be safely done because the only difference between 27 and 28 is an updated maven-source-plugin (fix heap space error)
     new 90057fe  SLING-5601 - The File System Classloader Console Plugin should allow wiping the classloader's cache
     new 556abbf  Update to parent pom 29
     new 9a259a4  [maven-release-plugin] prepare release org.apache.sling.commons.fsclassloader-1.0.4
     new 696b9d5  [maven-release-plugin] prepare for next development iteration
     new a5f8732  SLING-5980: Deduplicating the FSClassLoader cache clearing functionality and adding a JMX interface
     new afd3f78  Updating to use Classloader 1.4.0
     new 0d0253d  [maven-release-plugin] prepare release org.apache.sling.commons.fsclassloader-1.0.6
     new b67f5c7  [maven-release-plugin] copy for tag org.apache.sling.commons.fsclassloader-1.0.6

The 29 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


-- 
To stop receiving notification emails like this one, please contact
['"commits@sling.apache.org" <co...@sling.apache.org>'].

[sling-org-apache-sling-commons-fsclassloader] 03/05: Updating to use Classloader 1.4.0

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.commons.fsclassloader-1.0.6
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-fsclassloader.git

commit afd3f785cf211fe6d7cec05da3cff8b3c15a0b95
Author: Dan Klco <dk...@apache.org>
AuthorDate: Fri Feb 17 20:49:08 2017 +0000

    Updating to use Classloader 1.4.0
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader@1783466 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index f75ddc3..6c9b4c3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,7 +97,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.commons.classloader</artifactId>
-            <version>1.3.9-SNAPSHOT</version>
+            <version>1.4.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-commons-fsclassloader] 04/05: [maven-release-plugin] prepare release org.apache.sling.commons.fsclassloader-1.0.6

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.commons.fsclassloader-1.0.6
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-fsclassloader.git

commit 0d0253d82a665eb4514a97b7c6e9428388db2f85
Author: Dan Klco <dk...@apache.org>
AuthorDate: Fri Feb 17 20:53:06 2017 +0000

    [maven-release-plugin] prepare release org.apache.sling.commons.fsclassloader-1.0.6
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader@1783467 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 6c9b4c3..9bc92e4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
     </parent>
 
     <artifactId>org.apache.sling.commons.fsclassloader</artifactId>
-    <version>1.0.5-SNAPSHOT</version>
+    <version>1.0.6</version>
     <packaging>bundle</packaging>
 
     <name>Apache Sling Commons FileSystem ClassLoader</name>
@@ -38,9 +38,9 @@
     </description>
 
     <scm>
-        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader</connection>
-        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader</developerConnection>
-        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/fsclassloader</url>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.fsclassloader-1.0.6</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.fsclassloader-1.0.6</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/tags/org.apache.sling.commons.fsclassloader-1.0.6</url>
     </scm>
 
     <build>

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-commons-fsclassloader] 01/05: [maven-release-plugin] prepare for next development iteration

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.commons.fsclassloader-1.0.6
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-fsclassloader.git

commit 696b9d535acc94942cceda2c82cf8b759d42d194
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Dec 21 11:14:15 2016 +0000

    [maven-release-plugin] prepare for next development iteration
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader@1775399 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 650edd9..d79e248 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
     </parent>
 
     <artifactId>org.apache.sling.commons.fsclassloader</artifactId>
-    <version>1.0.4</version>
+    <version>1.0.5-SNAPSHOT</version>
     <packaging>bundle</packaging>
 
     <name>Apache Sling Commons FileSystem ClassLoader</name>
@@ -38,9 +38,9 @@
     </description>
 
     <scm>
-        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.fsclassloader-1.0.4</connection>
-        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.fsclassloader-1.0.4</developerConnection>
-        <url>http://svn.apache.org/viewvc/sling/tags/org.apache.sling.commons.fsclassloader-1.0.4</url>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/fsclassloader</url>
     </scm>
 
     <build>

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-commons-fsclassloader] 02/05: SLING-5980: Deduplicating the FSClassLoader cache clearing functionality and adding a JMX interface

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.commons.fsclassloader-1.0.6
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-fsclassloader.git

commit a5f873258b6519e702a044a80f093d4a945da7f0
Author: Dan Klco <dk...@apache.org>
AuthorDate: Fri Feb 17 19:06:12 2017 +0000

    SLING-5980: Deduplicating the FSClassLoader cache clearing functionality and adding a JMX interface
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader@1783452 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |   8 +-
 .../commons/fsclassloader/FSClassLoaderMBean.java  |  59 ++
 .../fsclassloader/impl/FSClassLoaderMBeanImpl.java | 112 ++++
 .../fsclassloader/impl/FSClassLoaderProvider.java  | 549 +++++++++-------
 .../impl/FSClassLoaderWebConsole.java              | 691 +++++++++------------
 .../commons/fsclassloader/impl/ScriptFiles.java    |  89 +++
 6 files changed, 883 insertions(+), 625 deletions(-)

diff --git a/pom.xml b/pom.xml
index d79e248..f75ddc3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,7 +97,13 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.commons.classloader</artifactId>
-            <version>1.3.0</version>
+            <version>1.3.9-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.1.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/src/main/java/org/apache/sling/commons/fsclassloader/FSClassLoaderMBean.java b/src/main/java/org/apache/sling/commons/fsclassloader/FSClassLoaderMBean.java
new file mode 100644
index 0000000..be49971
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/fsclassloader/FSClassLoaderMBean.java
@@ -0,0 +1,59 @@
+/*
+ * #%L
+ * ACS AEM Commons Bundle
+ * %%
+ * Copyright (C) 2017 - Adobe
+ * %%
+ * Licensed 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.
+ * #L%
+ */
+package org.apache.sling.commons.fsclassloader;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * MBean interface for interacting with the FSClassLoader
+ */
+public interface FSClassLoaderMBean {
+
+	/**
+	 * Clears the cache of compiled scripts from the FSClassLoader
+	 */
+	void clearCache();
+
+	/**
+	 * Gets the root directory at which the FSClassloader is creating it's
+	 * cached files
+	 * 
+	 * @return the file system classloader root
+	 */
+	String getFSClassLoaderRoot();
+
+	/**
+	 * Get the total count of scripts in the FSClassLoader cache
+	 * 
+	 * @return the total number of scripts
+	 * @throws IOException
+	 */
+	int getCachedScriptCount() throws IOException;
+
+	/**
+	 * Gets the scripts in the FSClassLoaderCache
+	 * 
+	 * @return the scripts from the FSClassLoaderCache
+	 * @throws IOException
+	 */
+	List<String> getCachedScripts() throws IOException;
+
+}
diff --git a/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderMBeanImpl.java b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderMBeanImpl.java
new file mode 100644
index 0000000..d9afe3c
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderMBeanImpl.java
@@ -0,0 +1,112 @@
+/*
+ * 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.commons.fsclassloader.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.commons.fsclassloader.FSClassLoaderMBean;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of the FSClassLoaderMBean interface
+ */
+public class FSClassLoaderMBeanImpl implements FSClassLoaderMBean {
+	private final BundleContext context;
+	private final FSClassLoaderProvider fsClassLoaderProvider;
+	private static final Logger log = LoggerFactory.getLogger(FSClassLoaderMBeanImpl.class);
+
+	public FSClassLoaderMBeanImpl(final FSClassLoaderProvider fsClassLoaderProvider, final BundleContext context) {
+		this.fsClassLoaderProvider = fsClassLoaderProvider;
+		this.context = context;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.apache.sling.commons.fsclassloader.FSClassLoaderMBean#
+	 * getCachedScriptCount()
+	 */
+	@Override
+	public int getCachedScriptCount() throws IOException {
+		return getScripts().size();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.apache.sling.commons.fsclassloader.FSClassLoaderMBean#
+	 * getCachedScripts()
+	 */
+	@Override
+	public List<String> getCachedScripts() {
+		List<String> scripts = new ArrayList<String>();
+		scripts.addAll(getScripts());
+		Collections.sort(scripts);
+		return scripts;
+	}
+
+	private Collection<String> getScripts() {
+		Collection<String> scripts = new HashSet<String>();
+		try {
+			Map<String, ScriptFiles> s = new LinkedHashMap<String, ScriptFiles>();
+			File root = new File(context.getDataFile(""), "classes");
+			if (root != null) {
+				FSClassLoaderWebConsole.readFiles(root, root, s);
+			}
+			scripts = s.keySet();
+		} catch (Exception e) {
+			log.warn("Exception retrieving scripts from FSClassLoader", e);
+		}
+		return scripts;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.apache.sling.commons.fsclassloader.FSClassLoaderMBean#clearCache()
+	 */
+	@Override
+	public void clearCache() {
+		fsClassLoaderProvider.delete("");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.apache.sling.commons.fsclassloader.FSClassLoaderMBean#
+	 * getFSClassLoaderRoot()
+	 */
+	@Override
+	public String getFSClassLoaderRoot() {
+		return new File(context.getDataFile(""), "classes").getAbsolutePath();
+	}
+
+}
diff --git a/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderProvider.java b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderProvider.java
index ee6b6bc..2b63f8d 100644
--- a/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderProvider.java
+++ b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderProvider.java
@@ -28,7 +28,13 @@ import java.io.OutputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -37,10 +43,17 @@ import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.commons.classloader.ClassLoaderWriter;
+import org.apache.sling.commons.classloader.ClassLoaderWriterListener;
 import org.apache.sling.commons.classloader.DynamicClassLoaderManager;
+import org.apache.sling.commons.fsclassloader.FSClassLoaderMBean;
+import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -51,240 +64,304 @@ import org.slf4j.LoggerFactory;
  *
  */
 @Component
-@Service(value={ClassLoaderWriter.class}, serviceFactory = true)
-@Property( name=Constants.SERVICE_RANKING, intValue=100)
-public class FSClassLoaderProvider
-    implements ClassLoaderWriter {
-
-    /** File root */
-    private File root;
-
-    /** File root URL */
-    private URL rootURL;
-
-    /** Current class loader */
-    private FSDynamicClassLoader loader;
-
-    private final Logger logger = LoggerFactory.getLogger(this.getClass());
-
-    @Reference(
-            referenceInterface = DynamicClassLoaderManager.class,
-            bind = "bindDynamicClassLoaderManager",
-            unbind = "unbindDynamicClassLoaderManager")
-    private ServiceReference dynamicClassLoaderManager;
-
-    /** The bundle asking for this service instance */
-    private Bundle callerBundle;
-
-    /**
-     * Activate this component.
-     * Create the root directory.
-     * @param componentContext
-     * @throws MalformedURLException
-     */
-    @Activate
-    protected void activate(final ComponentContext componentContext) throws MalformedURLException {
-        // get the file root
-        this.root = new File(componentContext.getBundleContext().getDataFile(""), "classes");
-        this.root.mkdirs();
-        this.rootURL = this.root.toURI().toURL();
-        this.callerBundle = componentContext.getUsingBundle();
-    }
-
-    /**
-     * Deactivate this component.
-     * Create the root directory.
-     */
-    @Deactivate
-    protected void deactivate() {
-        this.root = null;
-        this.rootURL = null;
-        this.destroyClassLoader();
-    }
-
-    /**
-     * Called to handle binding the DynamicClassLoaderManager service
-     * reference
-     */
-    @SuppressWarnings("unused")
-    private void bindDynamicClassLoaderManager(final ServiceReference ref) {
-        this.dynamicClassLoaderManager = ref;
-    }
-
-    /**
-     * Called to handle unbinding of the DynamicClassLoaderManager service
-     * reference
-     */
-    @SuppressWarnings("unused")
-    private void unbindDynamicClassLoaderManager(final ServiceReference ref) {
-        if (this.dynamicClassLoaderManager == ref) {
-            this.dynamicClassLoaderManager = null;
-        }
-    }
-
-    private void destroyClassLoader() {
-        final ClassLoader rcl = this.loader;
-        if (rcl != null) {
-            this.loader = null;
-
-            final ServiceReference localDynamicClassLoaderManager = this.dynamicClassLoaderManager;
-            final Bundle localCallerBundle = this.callerBundle;
-            if ( localDynamicClassLoaderManager != null && localCallerBundle != null ) {
-                localCallerBundle.getBundleContext().ungetService(localDynamicClassLoaderManager);
-            }
-        }
-    }
-
-    /**
-     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getClassLoader()
-     */
-    public ClassLoader getClassLoader() {
-        synchronized ( this ) {
-            if ( loader == null || !loader.isLive() ) {
-                this.destroyClassLoader();
-                // get the dynamic class loader for the bundle using this
-                // class loader writer
-                final DynamicClassLoaderManager dclm = (DynamicClassLoaderManager) this.callerBundle.getBundleContext().getService(
-                    this.dynamicClassLoaderManager);
-
-                loader = new FSDynamicClassLoader(new URL[] {this.rootURL}, dclm.getDynamicClassLoader());
-            }
-            return this.loader;
-        }
-    }
-
-    private void checkClassLoader(final String filePath) {
-        if ( filePath.endsWith(".class") ) {
-            // remove store directory and .class
-            final String path = filePath.substring(this.root.getAbsolutePath().length() + 1, filePath.length() - 6);
-            // convert to a class name
-            final String className = path.replace(File.separatorChar, '.');
-
-            synchronized ( this ) {
-                final FSDynamicClassLoader currentLoader = this.loader;
-                if ( currentLoader != null ) {
-                    currentLoader.check(className);
-                }
-            }
-        }
-    }
-
-    //---------- SCR Integration ----------------------------------------------
-
-    private boolean deleteRecursive(final File f, final List<String> names) {
-        if ( f.isDirectory() ) {
-            for(final File c : f.listFiles()) {
-                if ( !deleteRecursive(c, names) ) {
-                    return false;
-                }
-            }
-        }
-        names.add(f.getAbsolutePath());
-        return f.delete();
-    }
-
-    /**
-     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#delete(java.lang.String)
-     */
-    public boolean delete(final String name) {
-        final String path = cleanPath(name);
-        final File file = new File(path);
-        if ( file.exists() ) {
-            final List<String> names = new ArrayList<String>();
-            final boolean result = deleteRecursive(file, names);
-            logger.debug("Deleted {} : {}", name, result);
-            if ( result ) {
-                for(final String n : names ) {
-                    this.checkClassLoader(n);
-                }
-            }
-
-            return result;
-        }
-        // file does not exist so we return false
-        return false;
-    }
-
-    /**
-     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getOutputStream(java.lang.String)
-     */
-    public OutputStream getOutputStream(final String name) {
-        logger.debug("Get stream for {}", name);
-        final String path = cleanPath(name);
-        final File file = new File(path);
-        final File parentDir = file.getParentFile();
-        if ( !parentDir.exists() ) {
-            parentDir.mkdirs();
-        }
-        try {
-            if ( file.exists() ) {
-                this.checkClassLoader(path);
-            }
-            return new FileOutputStream(path);
-        } catch (FileNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#rename(java.lang.String, java.lang.String)
-     */
-    public boolean rename(final String oldName, final String newName) {
-        logger.debug("Rename {} to {}", oldName, newName);
-        final String oldPath = cleanPath(oldName);
-        final String newPath = cleanPath(newName);
-        final File old = new File(oldPath);
-        final boolean result = old.renameTo(new File(newPath));
-        if ( result ) {
-            this.checkClassLoader(oldPath);
-            this.checkClassLoader(newPath);
-        }
-        return result;
-    }
-
-    /**
-     * Clean the path by converting slashes to the correct format
-     * and prefixing the root directory.
-     * @param path The path
-     * @return The file path
-     */
-    private String cleanPath(String path) {
-        // replace backslash by slash
-        path = path.replace('\\', '/');
-
-        // cut off trailing slash
-        while (path.endsWith("/")) {
-            path = path.substring(0, path.length() - 1);
-        }
-        if ( File.separatorChar != '/') {
-            path = path.replace('/', File.separatorChar);
-        }
-        return this.root.getAbsolutePath() + path;
-    }
-
-    /**
-     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getInputStream(java.lang.String)
-     */
-    public InputStream getInputStream(final String name)
-    throws IOException {
-        logger.debug("Get input stream of {}", name);
-        final String path = cleanPath(name);
-        final File file = new File(path);
-        return new FileInputStream(file);
-    }
-
-    /**
-     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getLastModified(java.lang.String)
-     */
-    public long getLastModified(final String name) {
-        logger.debug("Get last modified of {}", name);
-        final String path = cleanPath(name);
-        final File file = new File(path);
-        if ( file.exists() ) {
-            return file.lastModified();
-        }
-
-        // fallback to "non-existant" in case of problems
-        return -1;
-    }
+@Service(value = { ClassLoaderWriter.class }, serviceFactory = true)
+@Property(name = Constants.SERVICE_RANKING, intValue = 100)
+public class FSClassLoaderProvider implements ClassLoaderWriter {
+
+	private static final String LISTENER_FILTER = "(" + Constants.OBJECTCLASS + "="
+			+ ClassLoaderWriterListener.class.getName() + ")";
+
+	/** File root */
+	private File root;
+
+	/** File root URL */
+	private URL rootURL;
+
+	/** Current class loader */
+	private FSDynamicClassLoader loader;
+
+	private static ServiceListener classLoaderWriterServiceListener;
+
+	private Map<Long, ServiceReference<ClassLoaderWriterListener>> classLoaderWriterListeners = new HashMap<Long, ServiceReference<ClassLoaderWriterListener>>();
+
+	private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+	@Reference(referenceInterface = DynamicClassLoaderManager.class, bind = "bindDynamicClassLoaderManager", unbind = "unbindDynamicClassLoaderManager")
+	private ServiceReference dynamicClassLoaderManager;
+
+	/** The bundle asking for this service instance */
+	private Bundle callerBundle;
+
+	private static ServiceRegistration<?> mbeanRegistration;
+
+	/**
+	 * Activate this component. Create the root directory.
+	 * 
+	 * @param componentContext
+	 * @throws MalformedURLException
+	 * @throws InvalidSyntaxException
+	 * @throws MalformedObjectNameException
+	 */
+	@Activate
+	protected void activate(final ComponentContext componentContext)
+			throws MalformedURLException, InvalidSyntaxException, MalformedObjectNameException {
+		// get the file root
+		this.root = new File(componentContext.getBundleContext().getDataFile(""), "classes");
+		this.root.mkdirs();
+		this.rootURL = this.root.toURI().toURL();
+		this.callerBundle = componentContext.getUsingBundle();
+
+		classLoaderWriterListeners.clear();
+		if (this.classLoaderWriterServiceListener != null) {
+			componentContext.getBundleContext().removeServiceListener(classLoaderWriterServiceListener);
+			classLoaderWriterServiceListener = null;
+		}
+		classLoaderWriterServiceListener = new ServiceListener() {
+			@Override
+			public void serviceChanged(ServiceEvent event) {
+				ServiceReference<ClassLoaderWriterListener> reference = (ServiceReference<ClassLoaderWriterListener>) event
+						.getServiceReference();
+				if (event.getType() == ServiceEvent.MODIFIED || event.getType() == ServiceEvent.REGISTERED) {
+					classLoaderWriterListeners.put(getId(reference), reference);
+				} else {
+					classLoaderWriterListeners.remove(getId(reference));
+				}
+			}
+
+			private Long getId(ServiceReference<ClassLoaderWriterListener> reference) {
+				return PropertiesUtil.toLong(reference.getProperty(Constants.SERVICE_ID), -1);
+			}
+		};
+		componentContext.getBundleContext().addServiceListener(classLoaderWriterServiceListener, LISTENER_FILTER);
+
+		// handle the MBean Installation
+		if (mbeanRegistration != null) {
+			mbeanRegistration.unregister();
+			mbeanRegistration = null;
+		}
+		Hashtable<String, String> jmxProps = new Hashtable<String, String>();
+		jmxProps.put("type", "ClassLoader");
+		jmxProps.put("name", "FSClassLoader");
+
+		final Hashtable<String, Object> mbeanProps = new Hashtable<String, Object>();
+		mbeanProps.put(Constants.SERVICE_DESCRIPTION, "Apache Sling FSClassLoader Controller Service");
+		mbeanProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+		mbeanProps.put("jmx.objectname", new ObjectName("org.apache.sling.classloader", jmxProps));
+		mbeanRegistration = componentContext.getBundleContext().registerService(FSClassLoaderMBean.class.getName(),
+				new FSClassLoaderMBeanImpl(this, componentContext.getBundleContext()), mbeanProps);
+	}
+
+	/**
+	 * Deactivate this component. Create the root directory.
+	 */
+	@Deactivate
+	protected void deactivate(ComponentContext componentContext) {
+		this.root = null;
+		this.rootURL = null;
+		this.destroyClassLoader();
+		if (this.classLoaderWriterServiceListener != null) {
+			componentContext.getBundleContext().removeServiceListener(classLoaderWriterServiceListener);
+		}
+		if (mbeanRegistration != null) {
+			mbeanRegistration.unregister();
+			mbeanRegistration = null;
+		}
+	}
+
+	/**
+	 * Called to handle binding the DynamicClassLoaderManager service reference
+	 */
+	@SuppressWarnings("unused")
+	private void bindDynamicClassLoaderManager(final ServiceReference ref) {
+		this.dynamicClassLoaderManager = ref;
+	}
+
+	/**
+	 * Called to handle unbinding of the DynamicClassLoaderManager service
+	 * reference
+	 */
+	@SuppressWarnings("unused")
+	private void unbindDynamicClassLoaderManager(final ServiceReference ref) {
+		if (this.dynamicClassLoaderManager == ref) {
+			this.dynamicClassLoaderManager = null;
+		}
+	}
+
+	private void destroyClassLoader() {
+		final ClassLoader rcl = this.loader;
+		if (rcl != null) {
+			this.loader = null;
+
+			final ServiceReference localDynamicClassLoaderManager = this.dynamicClassLoaderManager;
+			final Bundle localCallerBundle = this.callerBundle;
+			if (localDynamicClassLoaderManager != null && localCallerBundle != null) {
+				localCallerBundle.getBundleContext().ungetService(localDynamicClassLoaderManager);
+			}
+		}
+	}
+
+	/**
+	 * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getClassLoader()
+	 */
+	public ClassLoader getClassLoader() {
+		synchronized (this) {
+			if (loader == null || !loader.isLive()) {
+				this.destroyClassLoader();
+				// get the dynamic class loader for the bundle using this
+				// class loader writer
+				final DynamicClassLoaderManager dclm = (DynamicClassLoaderManager) this.callerBundle.getBundleContext()
+						.getService(this.dynamicClassLoaderManager);
+
+				loader = new FSDynamicClassLoader(new URL[] { this.rootURL }, dclm.getDynamicClassLoader());
+			}
+			return this.loader;
+		}
+	}
+
+	private void checkClassLoader(final String filePath) {
+		if (filePath.endsWith(".class")) {
+			// remove store directory and .class
+			final String path = filePath.substring(this.root.getAbsolutePath().length() + 1, filePath.length() - 6);
+			// convert to a class name
+			final String className = path.replace(File.separatorChar, '.');
+
+			synchronized (this) {
+				final FSDynamicClassLoader currentLoader = this.loader;
+				if (currentLoader != null) {
+					currentLoader.check(className);
+				}
+			}
+		}
+	}
+
+	// ---------- SCR Integration ----------------------------------------------
+
+	private boolean deleteRecursive(final File f, final List<String> names) {
+		if (f.isDirectory()) {
+			for (final File c : f.listFiles()) {
+				if (!deleteRecursive(c, names)) {
+					return false;
+				}
+			}
+		}
+		names.add(f.getAbsolutePath());
+		return f.delete();
+	}
+
+	/**
+	 * @see org.apache.sling.commons.classloader.ClassLoaderWriter#delete(java.lang.String)
+	 */
+	public boolean delete(final String name) {
+		final String path = cleanPath(name);
+		final File file = new File(path);
+		if (file.exists()) {
+			final List<String> names = new ArrayList<String>();
+			final boolean result = deleteRecursive(file, names);
+			logger.debug("Deleted {} : {}", name, result);
+			if (result) {
+				for (final String n : names) {
+					this.checkClassLoader(n);
+				}
+				for (ServiceReference<ClassLoaderWriterListener> reference : classLoaderWriterListeners.values()) {
+					if (reference != null) {
+						ClassLoaderWriterListener listener = callerBundle.getBundleContext().getService(reference);
+						if (listener != null) {
+							listener.onClassLoaderClear(name);
+						} else {
+							logger.warn("Found ClassLoaderWriterListener Service reference with no service bound");
+						}
+					}
+				}
+			}
+
+			return result;
+		}
+		// file does not exist so we return false
+		return false;
+	}
+
+	/**
+	 * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getOutputStream(java.lang.String)
+	 */
+	public OutputStream getOutputStream(final String name) {
+		logger.debug("Get stream for {}", name);
+		final String path = cleanPath(name);
+		final File file = new File(path);
+		final File parentDir = file.getParentFile();
+		if (!parentDir.exists()) {
+			parentDir.mkdirs();
+		}
+		try {
+			if (file.exists()) {
+				this.checkClassLoader(path);
+			}
+			return new FileOutputStream(path);
+		} catch (FileNotFoundException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * @see org.apache.sling.commons.classloader.ClassLoaderWriter#rename(java.lang.String,
+	 *      java.lang.String)
+	 */
+	public boolean rename(final String oldName, final String newName) {
+		logger.debug("Rename {} to {}", oldName, newName);
+		final String oldPath = cleanPath(oldName);
+		final String newPath = cleanPath(newName);
+		final File old = new File(oldPath);
+		final boolean result = old.renameTo(new File(newPath));
+		if (result) {
+			this.checkClassLoader(oldPath);
+			this.checkClassLoader(newPath);
+		}
+		return result;
+	}
+
+	/**
+	 * Clean the path by converting slashes to the correct format and prefixing
+	 * the root directory.
+	 * 
+	 * @param path
+	 *            The path
+	 * @return The file path
+	 */
+	private String cleanPath(String path) {
+		// replace backslash by slash
+		path = path.replace('\\', '/');
+
+		// cut off trailing slash
+		while (path.endsWith("/")) {
+			path = path.substring(0, path.length() - 1);
+		}
+		if (File.separatorChar != '/') {
+			path = path.replace('/', File.separatorChar);
+		}
+		return this.root.getAbsolutePath() + path;
+	}
+
+	/**
+	 * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getInputStream(java.lang.String)
+	 */
+	public InputStream getInputStream(final String name) throws IOException {
+		logger.debug("Get input stream of {}", name);
+		final String path = cleanPath(name);
+		final File file = new File(path);
+		return new FileInputStream(file);
+	}
+
+	/**
+	 * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getLastModified(java.lang.String)
+	 */
+	public long getLastModified(final String name) {
+		logger.debug("Get last modified of {}", name);
+		final String path = cleanPath(name);
+		final File file = new File(path);
+		if (file.exists()) {
+			return file.lastModified();
+		}
+
+		// fallback to "non-existant" in case of problems
+		return -1;
+	}
 }
diff --git a/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java
index eccbc29..0affacc 100644
--- a/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java
+++ b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderWebConsole.java
@@ -53,394 +53,309 @@ import org.slf4j.LoggerFactory;
  */
 @Component
 @Service
-@Properties({
-        @Property(name = "service.description", value = "Web Console for the FileSystem Class Loader"),
-        @Property(name = "service.vendor", value = "The Apache Software Foundation"),
-        @Property(name = "felix.webconsole.label", value = FSClassLoaderWebConsole.APP_ROOT),
-        @Property(name = "felix.webconsole.title", value = "File System Class Loader"),
-        @Property(name = "felix.webconsole.css", value = { FSClassLoaderWebConsole.RES_LOC
-                + "/prettify.css" }),
-        @Property(name = "felix.webconsole.category", value = "Sling") })
+@Properties({ @Property(name = "service.description", value = "Web Console for the FileSystem Class Loader"),
+		@Property(name = "service.vendor", value = "The Apache Software Foundation"),
+		@Property(name = "felix.webconsole.label", value = FSClassLoaderWebConsole.APP_ROOT),
+		@Property(name = "felix.webconsole.title", value = "File System Class Loader"),
+		@Property(name = "felix.webconsole.css", value = { FSClassLoaderWebConsole.RES_LOC + "/prettify.css" }),
+		@Property(name = "felix.webconsole.category", value = "Sling") })
 public class FSClassLoaderWebConsole extends AbstractWebConsolePlugin {
 
-    static final String APP_ROOT = "fsclassloader";
-
-    static final String RES_LOC = APP_ROOT + "/res/ui";
-    static final String POST_PARAM_CLEAR_CLASSLOADER = "clear";
-
-    private static final Logger LOG = LoggerFactory.getLogger(FSClassLoaderWebConsole.class);
-
-    @Reference(target = "(component.name=org.apache.sling.commons.fsclassloader.impl.FSClassLoaderProvider)")
-    private ClassLoaderWriter classLoaderWriter;
-
-    /**
-     * Represents a set of class, java and deps files for a script.
-     */
-    private static class ScriptFiles {
-
-        /**
-         * Gets the script associated with the file.
-         *
-         * @param file
-         *            the file to find the associate script
-         * @return the associated script
-         */
-        public static String getScript(File file) {
-            String relative = file.getAbsolutePath().substring(
-                    root.getAbsolutePath().length());
-            String script = remove(relative, "/org/apache/jsp");
-            script = remove(script, ".class");
-            script = remove(script, ".java");
-            script = remove(script, ".deps");
-            if (File.separatorChar == '\\') {
-                script = script.replace(File.separatorChar, '/');
-            }
-            return StringUtils.substringBeforeLast(script, "_") + "."
-                    + StringUtils.substringAfterLast(script, "_");
-        }
-
-        private static String remove(String orig, String rem) {
-            return orig.replace(rem, "");
-        }
-
-        private final String classFile;
-        private final String depsFile;
-
-        private final String javaFile;
-
-        private final String script;
-
-        public ScriptFiles(File file) {
-            script = getScript(file);
-
-            String relative = file.getAbsolutePath().substring(
-                    root.getAbsolutePath().length());
-
-            relative = remove(relative, ".class");
-            relative = remove(relative, ".deps");
-            relative = remove(relative, ".java");
-            classFile = relative + ".class";
-            depsFile = relative + ".deps";
-            javaFile = relative + ".java";
-        }
-
-        public String getClassFile() {
-            return classFile;
-        }
-
-        public String getDepsFile() {
-            return depsFile;
-        }
-
-        public String getJavaFile() {
-            return javaFile;
-        }
-
-        public String getScript() {
-            return script;
-        }
-
-    }
-
-    /**
-     * The root under which the class files are under
-     */
-    private static File root;
-
-    /**
-     * The serialization UID
-     */
-    private static final long serialVersionUID = -5728679635644481848L;
-
-    /**
-     * The servlet configuration
-     */
-    private ServletConfig config;
-
-    /**
-     * Activate this component. Create the root directory.
-     *
-     * @param componentContext the component context
-     * @throws MalformedURLException
-     */
-    @Activate
-    @SuppressWarnings("unused")
-    protected void activate(final ComponentContext componentContext)
-            throws MalformedURLException {
-        // get the file root
-        root = new File(componentContext.getBundleContext().getDataFile(""),
-                "classes");
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see javax.servlet.Servlet#destroy()
-     */
-    public void destroy() {
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest,
-     * javax.servlet.ServletResponse)
-     */
-    protected void doGet(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException {
-        String file = request.getParameter("download");
-        File toDownload = new File(root + file);
-        if (!StringUtils.isEmpty(file)) {
-            if (isValid(toDownload)) {
-                InputStream is = null;
-                try {
-                    is = new FileInputStream(toDownload);
-                    response.setHeader("Content-disposition",
-                            "attachment; filename=" + toDownload.getName());
-                    IOUtils.copy(is, response.getOutputStream());
-                } finally {
-                    IOUtils.closeQuietly(is);
-                    IOUtils.closeQuietly(response.getOutputStream());
-                }
-            } else {
-                response.sendError(404, "File " + file + " not found");
-            }
-        } else if (request.getRequestURI().endsWith(RES_LOC + "/prettify.css")) {
-            response.setContentType("text/css");
-            IOUtils.copy(
-                    getClass().getClassLoader().getResourceAsStream(
-                            "/res/ui/prettify.css"), response.getOutputStream());
-        } else if (request.getRequestURI().endsWith(RES_LOC + "/prettify.js")) {
-            response.setContentType("application/javascript");
-            IOUtils.copy(
-                    getClass().getClassLoader().getResourceAsStream(
-                            "/res/ui/prettify.js"), response.getOutputStream());
-        } else if (request.getRequestURI().endsWith(RES_LOC + "/fsclassloader.js")) {
-            response.setContentType("application/javascript");
-            IOUtils.copy(
-                    getClass().getClassLoader().getResourceAsStream(
-                            "/res/ui/fsclassloader.js"), response.getOutputStream());
-        }
-        else {
-            super.doGet(request, response);
-        }
-    }
-
-    @Override
-    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        String clear = req.getParameter(POST_PARAM_CLEAR_CLASSLOADER);
-        boolean shouldClear = Boolean.parseBoolean(clear);
-        if (shouldClear) {
-            if (classLoaderWriter != null) {
-                boolean result = classLoaderWriter.delete("");
-                if (result) {
-                    resp.getWriter().write("{ \"status\" : \"success\" }");
-                    resp.setStatus(HttpServletResponse.SC_OK);
-                } else {
-                    resp.getWriter().write("{ \"status\" : \"failure\", \"message\" : \"unable to clear classloader; check server log\" }");
-                    resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-                }
-            } else {
-                LOG.error("Cannot get a reference to org.apache.sling.commons.fsclassloader.impl.FSClassLoaderProvider");
-                resp.getWriter().write("{ \"status\" : \"failure\", \"message\" : \"unable to clear classloader; check server log\" }");
-                resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            }
-        } else {
-            resp.getWriter().write("{ \"status\" : \"failure\", \"message\" : \"invalid command\" }");
-            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getLabel()
-     */
-    @Override
-    public String getLabel() {
-        return "fsclassloader";
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see javax.servlet.Servlet#getServletConfig()
-     */
-    public ServletConfig getServletConfig() {
-        return this.config;
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see javax.servlet.Servlet#getServletInfo()
-     */
-    public String getServletInfo() {
-        return "";
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getTitle()
-     */
-    @Override
-    public String getTitle() {
-        return "File System Class Loader";
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
-     */
-    public void init(ServletConfig config) throws ServletException {
-        this.config = config;
-    }
-
-    /**
-     * Checks whether the specified file is a file and is underneath the root
-     * directory.
-     *
-     * @param file
-     *            the file to check
-     * @return false if not a file or not under the root directory, true
-     *         otherwise
-     * @throws IOException
-     */
-    private boolean isValid(File file) throws IOException {
-        if (file.isFile()) {
-            File parent = file.getCanonicalFile().getAbsoluteFile()
-                    .getParentFile();
-            while (parent != null) {
-                if (parent.getCanonicalPath().equals(root.getCanonicalPath())) {
-                    return true;
-                }
-                parent = parent.getParentFile();
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Reads all of the files under the current file.
-     *
-     * @param file
-     *            the root file
-     * @param scripts
-     *            the map of scripts
-     * @throws IOException
-     *             an exception occurs reading the files
-     */
-    private void readFiles(File file, Map<String, ScriptFiles> scripts)
-            throws IOException {
-        if (file.isDirectory()) {
-            File[] children = file.listFiles();
-            if (children != null) {
-                for (File f : children) {
-                    readFiles(f, scripts);
-                }
-            }
-        } else {
-            String script = ScriptFiles.getScript(file);
-            if (!scripts.containsKey(script)
-                    && file.getName().endsWith(".java")) {
-                scripts.put(script, new ScriptFiles(file));
-            }
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see
-     * org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax
-     * .servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
-    @Override
-    protected void renderContent(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException {
-        Map<String, ScriptFiles> scripts = new LinkedHashMap<String, ScriptFiles>();
-        readFiles(root, scripts);
-
-        Writer w = response.getWriter();
-
-        w.write("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + RES_LOC
-                + "/prettify.css\"></link>");
-        w.write("<script type=\"text/javascript\" src=\"" + RES_LOC
-                + "/prettify.js\"></script>");
-        w.write("<script type=\"text/javascript\" src=\"" + RES_LOC
-                + "/fsclassloader.js\"></script>");
-        w.write("<script>$(document).ready(prettyPrint);</script>");
-        w.write("<style>.prettyprint ol.linenums > li { list-style-type: decimal; } pre.prettyprint { white-space: pre-wrap; }</style>");
-        String file = request.getParameter("view");
-        File toView = new File(root + file);
-        w.write("<div id=\"classes\">");
-        if (!StringUtils.isEmpty(file)) {
-            if (isValid(toView)) {
-
-                w.write("<p class=\"statline ui-state-highlight\">Viewing Script: "
-                        + root + file + "</p><br/><br/>");
-
-                ScriptFiles scriptFiles = new ScriptFiles(toView);
-
-                w.write("<table class=\"nicetable ui-widget\">");
-                w.write("<tr class=\"header ui-widget-header\">");
-                w.write("<th>Script</th>");
-                w.write("<th>Class</th>");
-                w.write("<th>Deps</th>");
-                w.write("<th>Java</th>");
-                w.write("</tr>");
-                w.write("<tr class=\"ui-state-default\">");
-                w.write("<td>" + scriptFiles.getScript() + "</td>");
-                w.write("<td>[<a href=\"?download="
-                        + scriptFiles.getClassFile()
-                        + "\" target=\"_blank\">download</a>]</td>");
-                w.write("<td>[<a href=\"?download="
-                        + scriptFiles.getDepsFile()
-                        + "\" target=\"_blank\">download</a>]</td>");
-                w.write("<td>[<a href=\"?download="
-                        + scriptFiles.getJavaFile()
-                        + "\" target=\"_blank\">download</a>]</td>");
-                w.write("</tr>");
-                w.write("</table><br/><br/>");
-                InputStream is = null;
-                try {
-                    is = new FileInputStream(toView);
-                    String contents = IOUtils.toString(is, "UTF-8");
-                    w.write("<pre class=\"prettyprint linenums\">");
-                    w.write(StringEscapeUtils.escapeHtml4(contents));
-                    w.write("</pre>");
-                } finally {
-                    IOUtils.closeQuietly(is);
-                }
-            } else {
-                response.sendError(404, "File " + file + " not found");
-            }
-        } else {
-            w.write("<p class=\"statline ui-state-highlight\">File System ClassLoader Root: "
-                    + root + " <span style=\"float: right\"><button type='button' id='clear'>Clear Class Loader</button></span></p>");
-            if (scripts.values().size() > 0 ) {
-                w.write("<table class=\"nicetable ui-widget fsclassloader-has-classes\">");
-            } else {
-                w.write("<table class=\"nicetable ui-widget\">");
-            }
-            w.write("<tr class=\"header ui-widget-header\">");
-            w.write("<th>View</th>");
-            w.write("<th>Script</th>");
-            w.write("</tr>");
-            int i = 0;
-            for (ScriptFiles scriptFiles : scripts.values()) {
-                w.write("<tr class=\"" + (i % 2 == 0 ? "even" : "odd")
-                        + " ui-state-default\">");
-                w.write("<td>[<a href=\"?view=" + scriptFiles.getJavaFile()
-                        + "\">view</a>]</td>");
-                w.write("<td>" + scriptFiles.getScript() + "</td>");
-                w.write("</tr>");
-                i++;
-            }
-            w.write("</table>");
-        }
-        w.write("</div>");
-    }
+	static final String APP_ROOT = "fsclassloader";
+
+	static final String RES_LOC = APP_ROOT + "/res/ui";
+	static final String POST_PARAM_CLEAR_CLASSLOADER = "clear";
+
+	private static final Logger LOG = LoggerFactory.getLogger(FSClassLoaderWebConsole.class);
+
+	@Reference(target = "(component.name=org.apache.sling.commons.fsclassloader.impl.FSClassLoaderProvider)")
+	private ClassLoaderWriter classLoaderWriter;
+
+	/**
+	 * The root under which the class files are under
+	 */
+	private File root;
+
+	/**
+	 * The serialization UID
+	 */
+	private static final long serialVersionUID = -5728679635644481848L;
+
+	/**
+	 * The servlet configuration
+	 */
+	private ServletConfig config;
+
+	/**
+	 * Activate this component. Create the root directory.
+	 *
+	 * @param componentContext
+	 *            the component context
+	 * @throws MalformedURLException
+	 */
+	@Activate
+	protected void activate(final ComponentContext componentContext) throws MalformedURLException {
+		// get the file root
+		root = new File(componentContext.getBundleContext().getDataFile(""), "classes");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see javax.servlet.Servlet#destroy()
+	 */
+	public void destroy() {
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest,
+	 * javax.servlet.ServletResponse)
+	 */
+	protected void doGet(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		String file = request.getParameter("download");
+		File toDownload = new File(root + file);
+		if (!StringUtils.isEmpty(file)) {
+			if (isValid(toDownload)) {
+				InputStream is = null;
+				try {
+					is = new FileInputStream(toDownload);
+					response.setHeader("Content-disposition", "attachment; filename=" + toDownload.getName());
+					IOUtils.copy(is, response.getOutputStream());
+				} finally {
+					IOUtils.closeQuietly(is);
+					IOUtils.closeQuietly(response.getOutputStream());
+				}
+			} else {
+				response.sendError(404, "File " + file + " not found");
+			}
+		} else if (request.getRequestURI().endsWith(RES_LOC + "/prettify.css")) {
+			response.setContentType("text/css");
+			IOUtils.copy(getClass().getClassLoader().getResourceAsStream("/res/ui/prettify.css"),
+					response.getOutputStream());
+		} else if (request.getRequestURI().endsWith(RES_LOC + "/prettify.js")) {
+			response.setContentType("application/javascript");
+			IOUtils.copy(getClass().getClassLoader().getResourceAsStream("/res/ui/prettify.js"),
+					response.getOutputStream());
+		} else if (request.getRequestURI().endsWith(RES_LOC + "/fsclassloader.js")) {
+			response.setContentType("application/javascript");
+			IOUtils.copy(getClass().getClassLoader().getResourceAsStream("/res/ui/fsclassloader.js"),
+					response.getOutputStream());
+		} else {
+			super.doGet(request, response);
+		}
+	}
+
+	@Override
+	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+		String clear = req.getParameter(POST_PARAM_CLEAR_CLASSLOADER);
+		boolean shouldClear = Boolean.parseBoolean(clear);
+		if (shouldClear) {
+			if (classLoaderWriter != null) {
+				boolean result = classLoaderWriter.delete("");
+				if (result) {
+					resp.getWriter().write("{ \"status\" : \"success\" }");
+					resp.setStatus(HttpServletResponse.SC_OK);
+				} else {
+					resp.getWriter().write(
+							"{ \"status\" : \"failure\", \"message\" : \"unable to clear classloader; check server log\" }");
+					resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+				}
+			} else {
+				LOG.error(
+						"Cannot get a reference to org.apache.sling.commons.fsclassloader.impl.FSClassLoaderProvider");
+				resp.getWriter().write(
+						"{ \"status\" : \"failure\", \"message\" : \"unable to clear classloader; check server log\" }");
+				resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+			}
+		} else {
+			resp.getWriter().write("{ \"status\" : \"failure\", \"message\" : \"invalid command\" }");
+			resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getLabel()
+	 */
+	@Override
+	public String getLabel() {
+		return "fsclassloader";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see javax.servlet.Servlet#getServletConfig()
+	 */
+	public ServletConfig getServletConfig() {
+		return this.config;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see javax.servlet.Servlet#getServletInfo()
+	 */
+	public String getServletInfo() {
+		return "";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getTitle()
+	 */
+	@Override
+	public String getTitle() {
+		return "File System Class Loader";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
+	 */
+	public void init(ServletConfig config) throws ServletException {
+		this.config = config;
+	}
+
+	/**
+	 * Checks whether the specified file is a file and is underneath the root
+	 * directory.
+	 *
+	 * @param file
+	 *            the file to check
+	 * @return false if not a file or not under the root directory, true
+	 *         otherwise
+	 * @throws IOException
+	 */
+	private boolean isValid(File file) throws IOException {
+		if (file.isFile()) {
+			File parent = file.getCanonicalFile().getAbsoluteFile().getParentFile();
+			while (parent != null) {
+				if (parent.getCanonicalPath().equals(root.getCanonicalPath())) {
+					return true;
+				}
+				parent = parent.getParentFile();
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Reads all of the files under the current file.
+	 *
+	 * @param current
+	 *            the current file
+	 * @param root
+	 *            the root file
+	 * @param scripts
+	 *            the map of scripts
+	 * @throws IOException
+	 *             an exception occurs reading the files
+	 */
+	protected static void readFiles(File current, File root, Map<String, ScriptFiles> scripts) throws IOException {
+		if (current.isDirectory()) {
+			File[] children = current.listFiles();
+			if (children != null) {
+				for (File f : children) {
+					readFiles(f, root, scripts);
+				}
+			}
+		} else {
+			String script = ScriptFiles.getScript(root, current);
+			if (!scripts.containsKey(script) && current.getName().endsWith(".java")) {
+				scripts.put(script, new ScriptFiles(root, current));
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax
+	 * .servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+	 */
+	@Override
+	protected void renderContent(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		Map<String, ScriptFiles> scripts = new LinkedHashMap<String, ScriptFiles>();
+		readFiles(root, root, scripts);
+
+		Writer w = response.getWriter();
+
+		w.write("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + RES_LOC + "/prettify.css\"></link>");
+		w.write("<script type=\"text/javascript\" src=\"" + RES_LOC + "/prettify.js\"></script>");
+		w.write("<script type=\"text/javascript\" src=\"" + RES_LOC + "/fsclassloader.js\"></script>");
+		w.write("<script>$(document).ready(prettyPrint);</script>");
+		w.write("<style>.prettyprint ol.linenums > li { list-style-type: decimal; } pre.prettyprint { white-space: pre-wrap; }</style>");
+		String file = request.getParameter("view");
+		File toView = new File(root + file);
+		w.write("<div id=\"classes\">");
+		if (!StringUtils.isEmpty(file)) {
+			if (isValid(toView)) {
+
+				w.write("<p class=\"statline ui-state-highlight\">Viewing Script: " + root + file + "</p><br/><br/>");
+
+				ScriptFiles scriptFiles = new ScriptFiles(root, toView);
+
+				w.write("<table class=\"nicetable ui-widget\">");
+				w.write("<tr class=\"header ui-widget-header\">");
+				w.write("<th>Script</th>");
+				w.write("<th>Class</th>");
+				w.write("<th>Deps</th>");
+				w.write("<th>Java</th>");
+				w.write("</tr>");
+				w.write("<tr class=\"ui-state-default\">");
+				w.write("<td>" + scriptFiles.getScript() + "</td>");
+				w.write("<td>[<a href=\"?download=" + scriptFiles.getClassFile()
+						+ "\" target=\"_blank\">download</a>]</td>");
+				w.write("<td>[<a href=\"?download=" + scriptFiles.getDepsFile()
+						+ "\" target=\"_blank\">download</a>]</td>");
+				w.write("<td>[<a href=\"?download=" + scriptFiles.getJavaFile()
+						+ "\" target=\"_blank\">download</a>]</td>");
+				w.write("</tr>");
+				w.write("</table><br/><br/>");
+				InputStream is = null;
+				try {
+					is = new FileInputStream(toView);
+					String contents = IOUtils.toString(is, "UTF-8");
+					w.write("<pre class=\"prettyprint linenums\">");
+					w.write(StringEscapeUtils.escapeHtml4(contents));
+					w.write("</pre>");
+				} finally {
+					IOUtils.closeQuietly(is);
+				}
+			} else {
+				response.sendError(404, "File " + file + " not found");
+			}
+		} else {
+			w.write("<p class=\"statline ui-state-highlight\">File System ClassLoader Root: " + root
+					+ " <span style=\"float: right\"><button type='button' id='clear'>Clear Class Loader</button></span></p>");
+			if (scripts.values().size() > 0) {
+				w.write("<table class=\"nicetable ui-widget fsclassloader-has-classes\">");
+			} else {
+				w.write("<table class=\"nicetable ui-widget\">");
+			}
+			w.write("<tr class=\"header ui-widget-header\">");
+			w.write("<th>View</th>");
+			w.write("<th>Script</th>");
+			w.write("</tr>");
+			int i = 0;
+			for (ScriptFiles scriptFiles : scripts.values()) {
+				w.write("<tr class=\"" + (i % 2 == 0 ? "even" : "odd") + " ui-state-default\">");
+				w.write("<td>[<a href=\"?view=" + scriptFiles.getJavaFile() + "\">view</a>]</td>");
+				w.write("<td>" + scriptFiles.getScript() + "</td>");
+				w.write("</tr>");
+				i++;
+			}
+			w.write("</table>");
+		}
+		w.write("</div>");
+	}
 }
diff --git a/src/main/java/org/apache/sling/commons/fsclassloader/impl/ScriptFiles.java b/src/main/java/org/apache/sling/commons/fsclassloader/impl/ScriptFiles.java
new file mode 100644
index 0000000..80bde37
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/fsclassloader/impl/ScriptFiles.java
@@ -0,0 +1,89 @@
+/*
+ * 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.commons.fsclassloader.impl;
+
+import java.io.File;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Represents a set of class, java and deps files for a script.
+ */
+public class ScriptFiles {
+
+	/**
+	 * Gets the script associated with the file.
+	 *
+	 * @param file
+	 *            the file to find the associate script
+	 * @return the associated script
+	 */
+	public static String getScript(File root, File file) {
+		String relative = file.getAbsolutePath().substring(root.getAbsolutePath().length());
+		String script = remove(relative, "/org/apache/jsp");
+		script = remove(script, ".class");
+		script = remove(script, ".java");
+		script = remove(script, ".deps");
+		if (File.separatorChar == '\\') {
+			script = script.replace(File.separatorChar, '/');
+		}
+		return StringUtils.substringBeforeLast(script, "_") + "." + StringUtils.substringAfterLast(script, "_");
+	}
+
+	private static String remove(String orig, String rem) {
+		return orig.replace(rem, "");
+	}
+
+	private final String classFile;
+	private final String depsFile;
+
+	private final String javaFile;
+
+	private final String script;
+
+	public ScriptFiles(final File root, final File file) {
+		script = getScript(root, file);
+
+		String relative = file.getAbsolutePath().substring(root.getAbsolutePath().length());
+
+		relative = remove(relative, ".class");
+		relative = remove(relative, ".deps");
+		relative = remove(relative, ".java");
+		classFile = relative + ".class";
+		depsFile = relative + ".deps";
+		javaFile = relative + ".java";
+	}
+
+	public String getClassFile() {
+		return classFile;
+	}
+
+	public String getDepsFile() {
+		return depsFile;
+	}
+
+	public String getJavaFile() {
+		return javaFile;
+	}
+
+	public String getScript() {
+		return script;
+	}
+
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-commons-fsclassloader] 05/05: [maven-release-plugin] copy for tag org.apache.sling.commons.fsclassloader-1.0.6

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.commons.fsclassloader-1.0.6
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-fsclassloader.git

commit b67f5c7e28fc5f2db6418585700199623a870cb1
Author: Dan Klco <dk...@apache.org>
AuthorDate: Fri Feb 17 20:53:18 2017 +0000

    [maven-release-plugin] copy for tag org.apache.sling.commons.fsclassloader-1.0.6
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.fsclassloader-1.0.6@1783468 13f79535-47bb-0310-9956-ffa450edef68

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.