/* * Copyright 2013 NGDATA nv * Copyright 2008 Outerthought bvba and Schaubroeck nv * * 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. */ package org.lilyproject.runtime.classloading; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.lilyproject.runtime.repository.ArtifactNotFoundException; import org.lilyproject.runtime.repository.ArtifactRepository; public class ClassLoaderBuilder { private static Map<String, ClassLoader> classLoaderCache; private static final boolean classLoaderCacheEnabled; static { classLoaderCacheEnabled = System.getProperty("lilyruntime.cacheclassloaders") != null; if (classLoaderCacheEnabled) { classLoaderCache = new HashMap<String, ClassLoader>(); } } private ClassLoaderBuilder() { } public static synchronized ClassLoader build(List<ClasspathEntry> classpathEntries, ClassLoader parentClassLoader, ArtifactRepository repository) throws ArtifactNotFoundException, MalformedURLException { if (classLoaderCacheEnabled) { // // About the ClassLoader cache: // The ClassLoader cache was introduced to handle leaks in the 'Perm Gen' and 'Code Cache' // JVM memory spaces when repeatedly restarting Lily Runtime within the same JVM. In such cases, // on each restart Lily Runtime would construct new class loaders, hence the classes loaded through // them would be new and would stress those JVM memory spaces. // // The solution here is good enough for what it is intended to do (restarting the same Lily Runtime // app, unchanged, many times). There is no cache cleaning and the cache key calculation // is not perfect, so if you would enable the cache when starting a wide variety of Lily Runtime // apps within one VM or for reloading changed Lily Runtime apps, it could be problematic. // StringBuilder cacheKeyBuilder = new StringBuilder(2000); for (ClasspathEntry entry : classpathEntries) { cacheKeyBuilder.append(entry.getArtifactRef()).append("\u2603" /* unicode snowman as separator */); } // Add some identification of the parent class loader if (parentClassLoader instanceof URLClassLoader) { for (URL url : ((URLClassLoader)parentClassLoader).getURLs()) { cacheKeyBuilder.append(url.toString()); } } String cacheKey = cacheKeyBuilder.toString(); ClassLoader classLoader = classLoaderCache.get(cacheKey); if (classLoader == null) { Log log = LogFactory.getLog(ClassLoaderBuilder.class); log.debug("Creating and caching a new classloader"); classLoader = create(classpathEntries, parentClassLoader, repository); classLoaderCache.put(cacheKey, classLoader); } else if (classLoader.getParent() != parentClassLoader) { Log log = LogFactory.getLog(ClassLoaderBuilder.class); log.error("Lily Runtime ClassLoader cache: parentClassLoader of cache ClassLoader is different" + " from the specified one. Returning the cached one anyway."); } return classLoader; } else { return create(classpathEntries, parentClassLoader, repository); } } private static ClassLoader create(List<ClasspathEntry> classpathEntries, ClassLoader parentClassLoader, ArtifactRepository repository) throws ArtifactNotFoundException, MalformedURLException { List<URL> classpath = new ArrayList<URL>(); for (ClasspathEntry cpEntry : classpathEntries) { File resolvedFile = cpEntry.getArtifactRef().resolve(repository); classpath.add(resolvedFile.toURL()); } return new URLClassLoader(classpath.toArray(new URL[classpath.size()]), parentClassLoader); } }