This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-fsclassloader.git
commit ac4bcb595ad3845116a9d09ade79ddf4efe51676 Author: Dan Klco <[email protected]> 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@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 "[email protected]" <[email protected]>.
