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 63e103c6f30112a7a6c837ba57a31c9feff0d83b Author: Radu Cotescu <[email protected]> AuthorDate: Tue May 12 15:48:23 2015 +0000 SLING-4708 - Move the File System Classloader from contrib to bundles * moved fsclassloader to bundles and updated SCM info git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1678983 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 103 ++++++++ .../fsclassloader/impl/FSClassLoaderProvider.java | 270 +++++++++++++++++++++ .../fsclassloader/impl/FSDynamicClassLoader.java | 72 ++++++ 3 files changed, 445 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6f6630a --- /dev/null +++ b/pom.xml @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>22</version> + </parent> + + <artifactId>org.apache.sling.commons.fsclassloader</artifactId> + <version>1.0.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>Apache Sling Commons FileSystem ClassLoader</name> + <description> + The Sling Commons FileSystem ClassLoader bundle provides a dynamic class loader for reading + and writing class files from and to the file system. + </description> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/fsclassloader</developerConnection> + <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/fsclassloader</url> + </scm> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Private-Package> + org.apache.sling.commons.fsclassloader.impl + </Private-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <configuration> + <excludePackageNames> + org.apache.sling.commons.fsclassloader.impl + </excludePackageNames> + </configuration> + </plugin> + </plugins> + </reporting> + + <dependencies> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.classloader</artifactId> + <version>1.3.0</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> 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 new file mode 100644 index 0000000..74d2a2f --- /dev/null +++ b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSClassLoaderProvider.java @@ -0,0 +1,270 @@ +/* + * 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.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +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.DynamicClassLoaderManager; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The <code>FSClassLoaderProvider</code> is a dynamic class loader provider + * which uses the file system to store and read class files from. + * + */ +@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) { + synchronized ( this ) { + final FSDynamicClassLoader currentLoader = this.loader; + if ( currentLoader != null && 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, '.'); + currentLoader.check(className); + } + } + } + + //---------- SCR Integration ---------------------------------------------- + + /** + * @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 boolean result = file.delete(); + logger.debug("Deleted {} : {}", name,result); + if ( result ) { + this.checkClassLoader(file.getAbsolutePath()); + } + + 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/FSDynamicClassLoader.java b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSDynamicClassLoader.java new file mode 100644 index 0000000..d35ae3d --- /dev/null +++ b/src/main/java/org/apache/sling/commons/fsclassloader/impl/FSDynamicClassLoader.java @@ -0,0 +1,72 @@ +/* + * 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.net.URL; +import java.net.URLClassLoader; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.apache.sling.commons.classloader.DynamicClassLoader; + +public class FSDynamicClassLoader + extends URLClassLoader + implements DynamicClassLoader { + + private boolean isDirty = false; + + private final Set<String> hit = Collections.synchronizedSet(new HashSet<String>()); + private final Set<String> miss = Collections.synchronizedSet(new HashSet<String>()); + + private final DynamicClassLoader parentLoader; + + public FSDynamicClassLoader(final URL[] urls, final ClassLoader parent) { + super(urls, parent); + parentLoader = (parent instanceof DynamicClassLoader ? (DynamicClassLoader)parent : null); + } + + /** + * @see org.apache.sling.commons.classloader.DynamicClassLoader#isLive() + */ + public boolean isLive() { + return !isDirty && (parentLoader == null || parentLoader.isLive()); + } + + /** + * @see java.lang.ClassLoader#loadClass(java.lang.String) + */ + @Override + public Class<?> loadClass(final String name) throws ClassNotFoundException { + try { + final Class<?> c = super.loadClass(name); + this.hit.add(name); + return c; + } catch (final ClassNotFoundException cnfe) { + this.miss.add(name); + throw cnfe; + } + } + + public void check(final String className) { + if ( !this.isDirty ) { + this.isDirty = hit.contains(className) || miss.contains(className); + } + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
