/******************************************************************************* * Copyright (c) 2007, 2014 Massimiliano Ziccardi * * 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 it.jnrpe.server.plugins; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.List; import java.util.jar.JarFile; /** * A parent-last classloader that will try the child classloader first and then * the parent. This takes a fair bit of doing because java really prefers * parent-first. * * For those not familiar with class loading trickery, be wary */ class JNRPEClassLoader extends ClassLoader { /** * The list of classloader urls. */ private List<URL> classLoaderUrlList; /** * The child classloader. */ private ChildURLClassLoader childClassLoader; /** * This class allows me to call findClass on a classloader. */ private static class FindClassClassLoader extends ClassLoader { /** * Build the object specifing the parent classloader. * * @param parent * the parent classloader */ public FindClassClassLoader(final ClassLoader parent) { super(parent); } @Override public Class<?> findClass(final String name) throws ClassNotFoundException { return super.findClass(name); } } /** * This class delegates (child then parent) for the findClass method for a * URLClassLoader. We need this because findClass is protected in * URLClassLoader */ private static class ChildURLClassLoader extends URLClassLoader { /** * The parent classLoader. */ private final FindClassClassLoader realParent; /** * Builds the childclassloader. * * @param urls * The list of classloader urls * @param parent * The classloader parent */ public ChildURLClassLoader(final URL[] urls, final FindClassClassLoader parent) { super(urls, null); this.realParent = parent; } @Override public Class<?> findClass(final String name) throws ClassNotFoundException { try { // first try to use the URLClassLoader findClass return super.findClass(name); } catch (ClassNotFoundException e) { // if that fails, we ask our real parent classloader to load the // class (we give up) return realParent.loadClass(name); } } } /** * Constructs the object with a list of classloader urls. * * @param classpath * The list of urls */ public JNRPEClassLoader(final List<URL> classpath) { super(Thread.currentThread().getContextClassLoader()); classLoaderUrlList = classpath; final URL[] urls = classpath.toArray(new URL[classpath.size()]); final ClassLoader parent = getParent(); childClassLoader = AccessController.doPrivileged(new PrivilegedAction<ChildURLClassLoader>() { public ChildURLClassLoader run() { return new ChildURLClassLoader(urls, new FindClassClassLoader(parent)); } }); // childClassLoader = new ChildURLClassLoader(urls, // new FindClassClassLoader(this.getParent())); } @Override protected synchronized Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException { try { // first we try to find a class inside the child classloader return childClassLoader.findClass(name); } catch (ClassNotFoundException e) { // didn't find it, try the parent return super.loadClass(name, resolve); } } @Override protected URL findResource(final String name) { JarFile jf = null; try { for (URL u : classLoaderUrlList) { jf = new JarFile(u.getFile()); String resourceUrl = "jar:" + new File(u.getFile()).toURI() + "!/" + name; if (jf.getEntry("jnrpe_plugins.xml") != null) { return new URL(resourceUrl); } if (jf.getEntry("plugin.xml") != null) { return new URL(resourceUrl); } } return null; } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } finally { if (jf != null) { try { jf.close(); } catch (IOException e) { e.printStackTrace(); } } } } }