/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program 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. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.enterprise.server.plugin.pc; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * This is the classloader that will be the parent to all plugin classloaders. It will be created such that * it essentially hides a set of excluded classes which typically means this this root classloader (and its * children plugin classloaders) will allow the following to be loaded: * <ul> * <li>the plugin itself (including its third-party libraries)</li> * <li>core plugin container libraries required by all plugins, such as plugin API classes</li> * <li>plugin container jars that are configured to be unhidden</li> * </ul> * * @author John Mazzitelli */ //Note that this was an almost direct copy of the agent-side plugin container's RootPluginClassLoader at one time public class RootServerPluginClassLoader extends URLClassLoader { private final Log log = LogFactory.getLog(RootServerPluginClassLoader.class); private final Pattern classesToHideRegex; /** * Creates this classloader. <code>classesToHideRegexStr</code> is a regular expression to use to match against names * of classes to hide (i.e. not load). If a class that is to be loaded doesn't match the regex, it will be loaded * using parent-first semantics (i.e. it will first be searched in the parent classloader, and only if it isn't found * there will this classloader be checked for it). Otherwise, the class will be loaded using this classloader * only - the parent classloader will not be consulted so if this classloader does not have the class to be loaded, a * {@link ClassCastException} will be thrown. * * @param urls URLs to jar files where classes can be loaded by this classloader * @param parent the parent to this classloader, used when loading classes via parent-first semantics * @param classesToHideRegexStr regular expression(s) to use to match against names of classes to load. * if <code>null</code> or empty, no classes will be hidden, the parent will always * be consulted first to load the classes. * @throws PatternSyntaxException if the given regex is invalid (see {@link Pattern#compile(String)}) */ public RootServerPluginClassLoader(URL[] urls, ClassLoader parent, String... classesToHideRegexStr) { super((urls != null) ? urls : new URL[0], parent); Pattern pattern; if (classesToHideRegexStr != null && classesToHideRegexStr.length > 0) { StringBuilder fullPattern = new StringBuilder(); for (String regex : classesToHideRegexStr) { if (fullPattern.length() > 0) { fullPattern.append('|'); } fullPattern.append('(').append(regex).append(')'); } pattern = Pattern.compile(fullPattern.toString()); } else { pattern = null; } this.classesToHideRegex = pattern; log.debug("Root server plugin classloader: regex=[" + this.classesToHideRegex + "], urls=" + Arrays.asList((urls != null) ? urls : new URL[0])); } protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // first see if the class has already been loaded, if so, return the cached class Class<?> clazz = findLoadedClass(name); if (clazz == null) { // The class has not yet been loaded. // Check to see if the class does not match our regex; if it does not, the class is not to be "hidden" so we // will try to load it using parent-first semantics. This will give our parent classloader (i.e. the // plugin container, the agent and/or the embedding component) first dibs to load the class. If it // still can't be found, child classloaders will be checked. // // If the class does match the regex, the class is to be hidden, meaning if we can't load the class // ourself, that class is not to be loaded from any parent classloader. Only we can supply the class. if (this.classesToHideRegex == null || !this.classesToHideRegex.matcher(name).matches()) { try { clazz = super.loadClass(name, resolve); } catch (ClassNotFoundException cnfe) { if (log.isTraceEnabled()) { log.trace("Root plugin classloader cannot find unhidden class: " + name); } throw cnfe; } } else { try { clazz = findClass(name); if (resolve) { resolveClass(clazz); } } catch (ClassNotFoundException cnfe) { if (log.isTraceEnabled()) { log.trace("Root plugin classloader cannot find potentially hidden class: " + name); } throw cnfe; } } } return clazz; } @Override public URL getResource(String name) { URL res; // TODO: This doesn't follow the exact "hidden" semantics used when loading classes - is this a problem? if (this.classesToHideRegex == null) { res = super.getResource(name); } else { res = findResource(name); if (res == null) { res = super.getResource(name); } } return res; } }