All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.loader.EnvironmentClassLoader Maven / Gradle / Ivy

/*
 * Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.loader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.caucho.config.ConfigException;
import com.caucho.lifecycle.Lifecycle;
import com.caucho.loader.enhancer.ScanListener;
import com.caucho.loader.enhancer.ScanManager;
import com.caucho.loader.module.ArtifactManager;
import com.caucho.management.server.EnvironmentMXBean;
import com.caucho.util.Alarm;
import com.caucho.util.Crc64;
import com.caucho.util.CurrentTime;
import com.caucho.util.LruCache;
import com.caucho.util.ResinThreadPoolExecutor;
import com.caucho.vfs.Path;
import com.caucho.vfs.Vfs;

/**
 * Class loader which checks for changes in class files and automatically
 * picks up new jars.
 *
 * 

DynamicClassLoaders can be chained creating one virtual class loader. * From the perspective of the JDK, it's all one classloader. Internally, * the class loader chain searches like a classpath. */ public class EnvironmentClassLoader extends DynamicClassLoader { private static Logger _log; private static final Object _childListenerLock = new Object(); // listeners invoked at the start of any child environment private static EnvironmentLocal> _childListeners; // listeners invoked when a Loader is added private static EnvironmentLocal> _addLoaderListeners; // The owning bean private EnvironmentBean _owner; // Class loader specific attributes private Map _attributes = new ConcurrentHashMap(8); private ArrayList _scanListeners; private ArrayList _pendingScanRoots = new ArrayList(); private AtomicReference _artifactManagerRef = new AtomicReference(); private ArtifactManager _artifactManager; private ArrayList _packageList = new ArrayList(); // Array of listeners // server/306i - can't be weak reference, instead create WeakStopListener private ArrayList _listeners = new ArrayList(); private Map _resourceAliasMap; private LruCache _resourceCacheMap = new LruCache(256); private WeakStopListener _stopListener; // The state of the environment private volatile Lifecycle _lifecycle = new Lifecycle(); private boolean _isConfigComplete; private EnvironmentAdmin _admin; private Throwable _configException; //private static AtomicInteger _debugCounter = new AtomicInteger(); //private int _debugId = _debugCounter.incrementAndGet(); /** * Creates a new environment class loader. */ protected EnvironmentClassLoader(ClassLoader parent, String id) { this(parent, id, false); } /** * Creates a new environment class loader. */ protected EnvironmentClassLoader(ClassLoader parent, String id, boolean isRoot) { super(parent, true, isRoot); if (id != null) setId(id); // initializeEnvironment(); initListeners(); } /** * Creates a new environment class loader. */ public static EnvironmentClassLoader create() { ClassLoader parent = null; String id = null; return create(parent, id); } /** * Creates a new environment class loader. */ public static EnvironmentClassLoader create(String id) { ClassLoader parent = null; return create(parent, id); } /** * Creates a new environment class loader. */ public static EnvironmentClassLoader create(ClassLoader parent) { String id = null; return create(parent, id); } /** * Creates a new environment class loader. */ public static EnvironmentClassLoader create(ClassLoader parent, String id) { if (parent == null) parent = Thread.currentThread().getContextClassLoader(); ClassLoader systemClassLoader = getSystemClassLoaderSafe(); if (parent == null || isParent(parent, systemClassLoader)) parent = systemClassLoader; String className = System.getProperty("caucho.environment.class.loader"); if (className != null) { try { Class cl = Thread.currentThread().getContextClassLoader().loadClass(className); Constructor ctor = cl.getConstructor(new Class[] { ClassLoader.class, String.class}); Object instance = ctor.newInstance(parent, id); return (EnvironmentClassLoader) instance; } catch (Exception e) { e.printStackTrace(); } } return new EnvironmentClassLoader(parent, id); } private static boolean isParent(ClassLoader parent, ClassLoader child) { if (parent == null) return true; else if (child == null) return false; else if (parent.equals(child)) return true; else return isParent(parent, child.getParent()); } /** * Returns the environment's owner. */ public EnvironmentBean getOwner() { return _owner; } /** * Sets the environment's owner. */ public void setOwner(EnvironmentBean owner) { _owner = owner; } /** * Sets the config exception. */ public void setConfigException(Throwable e) { if (_configException == null) _configException = e; } /** * Gets the config exception. */ public Throwable getConfigException() { return _configException; } /** * Returns true if the environment is active */ public boolean isActive() { return _lifecycle.isActive(); } /** * Returns the admin */ public EnvironmentMXBean getAdmin() { return _admin; } /** * Initialize the environment. */ @Override public void init() { super.init(); initEnvironment(); } protected void initEnvironment() { } /** * Returns the named attributes */ public Object getAttribute(String name) { if (_attributes != null) return _attributes.get(name); else return null; } /** * Sets the named attributes */ public Object setAttribute(String name, Object obj) { if (obj == null) { if (_attributes == null) return null; else return _attributes.remove(name); } if (_attributes == null) _attributes = new ConcurrentHashMap(8); return _attributes.put(name, obj); } /** * Sets the named attributes */ public Object putIfAbsent(String name, Object obj) { if (obj == null) throw new NullPointerException(); if (_attributes == null) _attributes = new ConcurrentHashMap(8); return _attributes.putIfAbsent(name, obj); } /** * Removes the named attributes */ public Object removeAttribute(String name) { if (_attributes == null) return null; else return _attributes.remove(name); } // // resources // /** * Overrides getResource to implement caching. */ @Override public URL getResource(String name) { ResourceEntry entry = _resourceCacheMap.get(name); if (entry == null || entry.isModified()) { URL resource = super.getResource(name); entry = new ResourceEntry(resource); _resourceCacheMap.put(name, entry); } return entry.getResource(); } /** * Overrides getResource to implement caching. */ @Override public InputStream getResourceAsStream(String name) { ResourceEntry entry = _resourceCacheMap.get(name); if (entry == null || entry.isModified()) { URL resource = super.getResource(name); entry = new ResourceEntry(resource); _resourceCacheMap.put(name, entry); } return entry.getResourceAsStream(); } // // resource aliases // public void putResourceAlias(String name, String actualName) { if (_resourceAliasMap == null) _resourceAliasMap = new ConcurrentHashMap(); _resourceAliasMap.put(name, actualName); } @Override public String getResourceAlias(String name) { if (_resourceAliasMap != null) { String actualName = _resourceAliasMap.get(name); if (actualName != null) return actualName; } return null; } /** * Adds a listener to detect environment lifecycle changes. */ public void addListener(EnvironmentListener listener) { synchronized (_listeners) { for (int i = _listeners.size() - 1; i >= 0; i--) { EnvironmentListener oldListener = _listeners.get(i); if (listener == oldListener) { return; } else if (oldListener == null) _listeners.remove(i); } _listeners.add(listener); } if (_lifecycle.isStarting()) { listener.environmentBind(this); } if (_lifecycle.isStarting() && _isConfigComplete) { listener.environmentStart(this); } } /** * Adds self as a listener. */ private void initListeners() { ClassLoader parent = getParent(); for (; parent != null; parent = parent.getParent()) { if (parent instanceof EnvironmentClassLoader) { EnvironmentClassLoader loader = (EnvironmentClassLoader) parent; if (_stopListener == null) _stopListener = new WeakStopListener(this); loader.addListener(_stopListener); return; } } } /** * Adds a listener to detect environment lifecycle changes. */ public void removeListener(EnvironmentListener listener) { ArrayList listeners = _listeners; if (_listeners == null) return; synchronized (listeners) { for (int i = listeners.size() - 1; i >= 0; i--) { EnvironmentListener oldListener = listeners.get(i); if (listener == oldListener) { listeners.remove(i); return; } else if (oldListener == null) { listeners.remove(i); } } } } /** * Adds a child listener. */ void addChildListener(EnvironmentListener listener) { synchronized (_childListenerLock) { if (_childListeners == null) _childListeners = new EnvironmentLocal>(); ArrayList listeners = _childListeners.getLevel(this); if (listeners == null) { listeners = new ArrayList(); _childListeners.set(listeners, this); } listeners.add(listener); } if (_lifecycle.isStarting() && _isConfigComplete) { listener.environmentStart(this); } } /** * Removes a child listener. */ void removeChildListener(EnvironmentListener listener) { synchronized (_childListenerLock) { if (_childListeners == null) return; ArrayList listeners = _childListeners.getLevel(this); if (listeners != null) listeners.remove(listener); } } /** * Returns the listeners. */ protected ArrayList getEnvironmentListeners() { ArrayList listeners; listeners = new ArrayList(); // add the descendant listeners if (_childListeners != null) { synchronized (_childListenerLock) { ClassLoader loader; for (loader = this; loader != null; loader = loader.getParent()) { if (loader instanceof EnvironmentClassLoader) { ArrayList childListeners; childListeners = _childListeners.getLevel(loader); if (childListeners != null) listeners.addAll(childListeners); } } } } if (_listeners == null) return listeners; ArrayList envListeners = _listeners; if (envListeners != null) { synchronized (envListeners) { for (int i = 0; i < envListeners.size(); i++) { EnvironmentListener listener = envListeners.get(i); if (listener != null) listeners.add(listener); else { envListeners.remove(i); i--; } } } } return listeners; } /** * Adds a child listener. */ public void addLoaderListener(AddLoaderListener listener) { synchronized (_childListenerLock) { if (_addLoaderListeners == null) _addLoaderListeners = new EnvironmentLocal>(); ArrayList listeners = _addLoaderListeners.getLevel(this); if (listeners == null) { listeners = new ArrayList(); _addLoaderListeners.set(listeners, this); } if (! listeners.contains(listener)) { listeners.add(listener); } } listener.addLoader(this); } /** * Returns the listeners. */ protected ArrayList getLoaderListeners() { ArrayList listeners; listeners = new ArrayList(); if (_addLoaderListeners == null) return listeners; // add the descendent listeners synchronized (_childListenerLock) { ClassLoader loader; for (loader = this; loader != null; loader = loader.getParent()) { if (loader instanceof EnvironmentClassLoader) { ArrayList childListeners; childListeners = _addLoaderListeners.getLevel(loader); if (childListeners != null) listeners.addAll(childListeners); } } } return listeners; } /** * Adds a listener to detect class loader changes. */ @Override protected void configureEnhancerEvent() { ArrayList listeners = getLoaderListeners(); for (int i = 0; listeners != null && i < listeners.size(); i++) { AddLoaderListener listener = listeners.get(i); if (listener.isEnhancer()) listeners.get(i).addLoader(this); } } /** * Adds a listener to detect class loader changes. */ @Override protected void configurePostEnhancerEvent() { ArrayList listeners = getLoaderListeners(); for (int i = 0; listeners != null && i < listeners.size(); i++) { AddLoaderListener listener = listeners.get(i); if (! listener.isEnhancer()) listeners.get(i).addLoader(this); } } /** * Adds the URL to the URLClassLoader. */ @Override public void addURL(URL url) { if (containsURL(url)) return; super.addURL(url); _pendingScanRoots.add(new ScanRoot(url, null)); } /** * Adds a virtual module root for scanning. * * @param rootPackage */ public void addScanPackage(URL url, String rootPackage) { if (! containsURL(url)) { super.addURL(url); } _packageList.add(rootPackage); _pendingScanRoots.add(new ScanRoot(url, rootPackage)); sendAddLoaderEvent(); } /** * Add the custom packages to the classloader hash. */ @Override public String getHash() { String superHash = super.getHash(); // ioc/0p61 - package needed for hash to enable scan long crc = Crc64.generate(superHash); for (String pkg : _packageList) { crc = Crc64.generate(crc, pkg); } return Long.toHexString(crc); } /** * Tells the classloader to scan the root classpath. */ @Override public void addScanRoot() { super.addScanRoot(); addScanRoot(getParent()); } private void addScanRoot(ClassLoader loader) { if (loader == null) return; addScanRoot(loader.getParent()); if (loader instanceof URLClassLoader) { URLClassLoader urlParent = (URLClassLoader) loader; for (URL url : urlParent.getURLs()) { // String name = url.toString(); // ejb/0e01 //if (name.endsWith(".jar")) { _pendingScanRoots.add(new ScanRoot(url, null)); //} } } } /** * Adds a scan listener. */ public void addScanListener(ScanListener listener) { if (_scanListeners == null) { _scanListeners = new ArrayList(); } int i = 0; for (; i < _scanListeners.size(); i++) { if (listener.getScanPriority() < _scanListeners.get(i).getScanPriority()) { break; } } _scanListeners.add(i, listener); ArrayList urlList = new ArrayList(); for (URL url : getURLs()) { if (isScanRootAvailable(url)) { urlList.add(url); } } if (urlList.size() > 0) { try { make(); } catch (Exception e) { log().log(Level.WARNING, e.toString(), e); if (_configException == null) _configException = e; } ArrayList selfList = new ArrayList(); selfList.add(listener); ScanManager scanManager = new ScanManager(selfList); for (URL url : urlList) { scanManager.scan(this, url, null); } } } private boolean isScanRootAvailable(URL url) { for (ScanRoot scanRoot : _pendingScanRoots) { if (url.equals(scanRoot.getUrl())) return false; } return true; } /** * Returns the artifact manager */ public ArtifactManager createArtifactManager() { if (_artifactManager == null) { ArtifactManager manager = new ArtifactManager(this); _artifactManagerRef.compareAndSet(null, manager); _artifactManager = _artifactManagerRef.get(); } return _artifactManager; } /** * Returns the artifact manager */ public ArtifactManager getArtifactManager() { return _artifactManager; } /** * Returns any import class, e.g. from an artifact */ @Override protected Class findImportClass(String name) { if (_artifactManager != null) return _artifactManager.findImportClass(name); else return null; } /** * Get resource from an artifact */ @Override protected URL getImportResource(String name) { if (_artifactManager != null) return _artifactManager.getImportResource(name); else return null; } @Override protected void buildImportClassPath(ArrayList cp) { if (_artifactManager != null) _artifactManager.buildImportClassPath(cp); } /** * Applies the action to all visible environment modules. The * action may apply to the same environment more than once. */ public void applyVisibleModules(EnvironmentApply apply) { apply.apply(this); for (ClassLoader parent = getParent(); parent != null; parent = parent.getParent()) { if (parent instanceof EnvironmentClassLoader) { EnvironmentClassLoader env = (EnvironmentClassLoader) parent; env.applyVisibleModules(apply); break; } } if (_artifactManager != null) _artifactManager.applyVisibleModules(apply); } /** * Called when the completes. */ @Override public void validate() { super.validate(); } @Override public void scan() { configureEnhancerEvent(); ArrayList rootList = new ArrayList(_pendingScanRoots); _pendingScanRoots.clear(); try { int rootListSize = rootList.size(); if (_scanListeners != null && rootListSize > 0) { try { make(); } catch (Exception e) { log().log(Level.WARNING, e.toString(), e); if (_configException == null) { _configException = e; } } ScanManager scanManager = new ScanManager(_scanListeners); for (int i = 0; i < rootListSize; i++) { ScanRoot root = rootList.get(i); scanManager.scan(this, root.getUrl(), root.getPackageName()); } } // configureEnhancerEvent(); } catch (Exception e) { if (_configException == null) _configException = e; throw ConfigException.create(e); } } /** * Starts the config phase of the environment. */ private void config() { sendAddLoaderEvent(); ArrayList listeners = getEnvironmentListeners(); int size = listeners.size(); for (int i = 0; listeners != null && i < size; i++) { EnvironmentListener listener = listeners.get(i); if (listener instanceof EnvironmentEnhancerListener) { EnvironmentEnhancerListener enhancerListener = (EnvironmentEnhancerListener) listener; enhancerListener.environmentConfigureEnhancer(this); } } for (int i = 0; listeners != null && i < size; i++) { EnvironmentListener listener = listeners.get(i); listener.environmentConfigure(this); } // _isConfigComplete = true; } /** * Starts the config phase of the environment. */ private void bind() { config(); ArrayList listeners = getEnvironmentListeners(); int size = listeners.size(); for (int i = 0; listeners != null && i < size; i++) { EnvironmentListener listener = listeners.get(i); listener.environmentBind(this); } _isConfigComplete = true; } /** * Marks the environment of the class loader as started. The * class loader itself doesn't use this, but a callback might. */ public void start() { if (! _lifecycle.toStarting()) { startListeners(); return; } sendAddLoaderEvent(); bind(); if (_artifactManager != null) _artifactManager.start(); startListeners(); _admin = new EnvironmentAdmin(this); _admin.register(); _lifecycle.toActive(); } private void startListeners() { ArrayList listeners = getEnvironmentListeners(); int size = listeners.size(); for (int i = 0; listeners != null && i < size; i++) { EnvironmentListener listener = listeners.get(i); listener.environmentStart(this); } } /** * Stops the environment, closing down any resources. * * The resources are closed in the reverse order that they're started */ @Override public void stop() { if (! _lifecycle.toStop()) return; ArrayList listeners = getEnvironmentListeners(); Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); thread.setContextClassLoader(this); try { // closing down in reverse if (listeners != null) { for (int i = listeners.size() - 1; i >= 0; i--) { EnvironmentListener listener = listeners.get(i); try { listener.environmentStop(this); } catch (Throwable e) { log().log(Level.WARNING, e.toString(), e); } } } super.stop(); } finally { thread.setContextClassLoader(oldLoader); // drain the thread pool for GC ResinThreadPoolExecutor.getThreadPool().stopEnvironment(this); } } /** * Destroys the class loader. */ @Override public void destroy() { try { WeakStopListener stopListener = _stopListener; _stopListener = null; super.destroy(); ClassLoader parent = getParent(); for (; parent != null; parent = parent.getParent()) { if (parent instanceof EnvironmentClassLoader) { EnvironmentClassLoader loader = (EnvironmentClassLoader) parent; loader.removeListener(stopListener); } } } finally { _owner = null; _attributes = null; _listeners = null; _scanListeners = null; _artifactManager = null; _stopListener = null; EnvironmentAdmin admin = _admin; _admin = null; if (admin != null) admin.unregister(); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append("["); sb.append(getId()); if (! _lifecycle.isActive()) { sb.append(","); sb.append(_lifecycle.getStateName()); } sb.append("]"); return sb.toString(); } private static final Logger log() { if (_log == null) _log = Logger.getLogger(EnvironmentClassLoader.class.getName()); return _log; } private static ClassLoader getSystemClassLoaderSafe() { try { return ClassLoader.getSystemClassLoader(); } catch (Exception e) { } return EnvironmentClassLoader.class.getClassLoader(); } static class ScanRoot { private final URL _url; private final String _pkg; ScanRoot(URL url, String pkg) { _url = url; _pkg = pkg; } URL getUrl() { return _url; } String getPackageName() { return _pkg; } @Override public String toString() { return getClass().getSimpleName() + "[" + _url + "," + _pkg + "]"; } } class ResourceEntry { private URL _url; private long _expireTime; private Path _path; private boolean _isPathChecked; ResourceEntry(URL url) { _url = url; if (isDirectoryLoader()) _expireTime = CurrentTime.getCurrentTime() + getDependencyCheckInterval(); else _expireTime = Long.MAX_VALUE / 2; } public boolean isModified() { return _expireTime < CurrentTime.getCurrentTime(); } public URL getResource() { return _url; } public InputStream getResourceAsStream() { if (_url == null) return null; if (! _isPathChecked) { String urlString = URLDecoder.decode(_url.toString()); if (urlString.startsWith("file:") || urlString.startsWith("jar:")) _path = Vfs.getPwd(EnvironmentClassLoader.this).lookup(urlString); _isPathChecked = true; } try { if (_path != null) return _path.openRead(); else return _url.openStream(); } catch (IOException e) { log().log(Level.FINE, e.toString(), e); return null; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy