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:44 UTC
[sling-org-apache-sling-commons-fsclassloader] 02/05: SLING-5980:
Deduplicating the FSClassLoader cache clearing functionality and adding a
JMX interface
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>.