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

jodd.util.cl.ExtendedURLClassLoader Maven / Gradle / Ivy

// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.util.cl;

import jodd.system.SystemUtil;
import jodd.util.ArraysUtil;
import jodd.util.StringUtil;
import jodd.util.Wildcard;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

/**
 * Class loader that offers two loading strategies: parent-first and parent-last.
 * The strategy defines the class loading order between this classloader and parent class loader;
 * i.e. defines the first class loader to be used for loading classes.
 * Extends URLClassLoader to provide just minimal set of modifications.
 * 

* Either way, you can specify wildcard rules to force some behavior - like loading some package/class * exclusively with specific loader. There are the following groups: *

    *
  • parent-only - classes will be loaded only with parent classloader.
  • *
  • loader-only - classes will be loaded only with this loader.
  • *
  • default - classes will be loaded as specified by strategy (no need to define).
  • *
*

* The order of matching is also set by loading strategy. For parent-first strategy, * loader group rules will be checked first. For parent-last strategy, * parent group rules will be checked first. *

* By default, the list of parent-only group is populated with * {@link jodd.system.SystemInfo#getJrePackages()} JRE packages}. *

* When parent-last strategy is used, be aware how you use * {@link jodd.util.ClassLoaderUtil} as it is designed to follow parent-first * strategy. Use with caution as setting parent-last violates the * class loader hierarchy. */ public class ExtendedURLClassLoader extends URLClassLoader { protected final ClassLoader parentClassLoader; protected String[] parentOnlyRules; protected String[] loaderOnlyRules; protected final boolean parentFirst; protected boolean matchResourcesAsPackages = true; public ExtendedURLClassLoader(final URL[] classpath, final ClassLoader parent, final boolean parentFirst) { this(classpath, parent, parentFirst, true); } /** * Creates class loader with given loading strategy. */ public ExtendedURLClassLoader( final URL[] classpath, final ClassLoader parent, final boolean parentFirst, final boolean excludeJrePackagesFromLoader) { super(classpath, parent); this.parentFirst = parentFirst; if (parent == null) { throw new IllegalArgumentException("Parent classloader not specified"); } parentClassLoader = parent; parentOnlyRules = new String[0]; loaderOnlyRules = new String[0]; if (excludeJrePackagesFromLoader) { final String[] corePackages = SystemUtil.info().getJrePackages(); for (String corePackage : corePackages) { if (corePackage.equals("javax")) { // javax is NOT forbidden continue; } addParentOnlyRules(corePackage + ".*"); } } } // ---------------------------------------------------------------- rules /** * Adds parent only rules for classes which must be loaded on the * parent loader. */ public void addParentOnlyRules(final String... packages) { parentOnlyRules = ArraysUtil.join(parentOnlyRules, packages); } /** * Adds loader-only rules for classes which must be loaded using this * loader. */ public void addLoaderOnlyRules(final String... packages) { loaderOnlyRules = ArraysUtil.join(loaderOnlyRules, packages); } /** * When set, resources will be matched in the same way as packages. * If disabled, resources must be matched only with separate rules * that uses "/". */ public void setMatchResourcesAsPackages(final boolean matchResourcesAsPackages) { this.matchResourcesAsPackages = matchResourcesAsPackages; } /** * Returns true if class or resource name matches * at least one package rule from the list. */ protected boolean isMatchingRules(final String name, final String... rules) { for (String rule : rules) { if (Wildcard.equalsOrMatch(name, rule)) { return true; } } return false; } // ---------------------------------------------------------------- resolver protected static class Loading { public Loading(final boolean withParent, final boolean withLoader) { this.withParent = withParent; this.withLoader = withLoader; } protected final boolean withParent; protected final boolean withLoader; } /** * Resolves loading rules. */ protected Loading resolveLoading(final boolean parentFirstStrategy, final String className) { boolean withParent = true; boolean withLoader = true; if (parentFirstStrategy) { if (isMatchingRules(className, loaderOnlyRules)) { withParent = false; } else if (isMatchingRules(className, parentOnlyRules)) { withLoader = false; } } else { if (isMatchingRules(className, parentOnlyRules)) { withLoader = false; } else if (isMatchingRules(className, loaderOnlyRules)) { withParent = false; } } return new Loading(withParent, withLoader); } /** * Resolves resources. */ protected Loading resolveResourceLoading(final boolean parentFirstStrategy, String resourceName) { if (matchResourcesAsPackages) { resourceName = StringUtil.replaceChar(resourceName, '/', '.'); } return resolveLoading(parentFirstStrategy, resourceName); } // ---------------------------------------------------------------- overrides /** * Loads class using parent-first or parent-last strategy. */ @Override protected synchronized Class loadClass(final String className, final boolean resolve) throws ClassNotFoundException { // check first if the class has already been loaded Class c = findLoadedClass(className); if (c != null) { if (resolve) { resolveClass(c); } return c; } // class not loaded yet Loading loading = resolveLoading(parentFirst, className); if (parentFirst) { // PARENT FIRST if (loading.withParent) { try { c = parentClassLoader.loadClass(className); } catch (ClassNotFoundException ignore) { } } if (c == null) { if (loading.withLoader) { c = this.findClass(className); } else { throw new ClassNotFoundException("Class not found: " + className); } } } else { // THIS FIRST if (loading.withLoader) { try { c = this.findClass(className); } catch (ClassNotFoundException ignore) { } } if (c == null) { if (loading.withParent) { c = parentClassLoader.loadClass(className); } else { throw new ClassNotFoundException("Class not found: " + className); } } } if (resolve) { resolveClass(c); } return c; } /** * Returns a resource using parent-first or parent-last strategy. */ @Override public URL getResource(final String resourceName) { URL url = null; Loading loading = resolveResourceLoading(parentFirst, resourceName); if (parentFirst) { // PARENT FIRST if (loading.withParent) { url = parentClassLoader.getResource(resourceName); } if (url == null) { if (loading.withLoader) { url = this.findResource(resourceName); } } } else { // THIS FIRST if (loading.withLoader) { url = this.findResource(resourceName); } if (url == null) { if (loading.withParent) { url = parentClassLoader.getResource(resourceName); } } } return url; } @Override public Enumeration getResources(final String resourceName) throws IOException { final List urls = new ArrayList<>(); Enumeration loaderUrls = this.findResources(resourceName); Enumeration parentUrls = parentClassLoader.getResources(resourceName); Loading loading = resolveResourceLoading(parentFirst, resourceName); if (parentFirst) { if (loading.withParent) { while (parentUrls.hasMoreElements()) { urls.add(parentUrls.nextElement()); } } if (loading.withLoader) { while (loaderUrls.hasMoreElements()) { urls.add(loaderUrls.nextElement()); } } } else { if (loading.withLoader) { while (loaderUrls.hasMoreElements()) { urls.add(loaderUrls.nextElement()); } } if (loading.withParent) { while (parentUrls.hasMoreElements()) { urls.add(parentUrls.nextElement()); } } } return new Enumeration() { Iterator iterator = urls.iterator(); @Override public boolean hasMoreElements() { return iterator.hasNext(); } @Override public URL nextElement() { return iterator.next(); } }; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy