/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2014 ForgeRock AS. */ package org.identityconnectors.framework.impl.api.local; import static org.identityconnectors.common.CollectionUtil.newReadOnlyMap; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.Map; class BundleClassLoader extends URLClassLoader { private static final String FRAMEWORK_PACKAGE = "org.identityconnectors.framework"; // The set of packages a connector is allowed to access from the // parent class loader. private static final String[] ALLOWED_FRAMEWORK_PACKAGES = { FRAMEWORK_PACKAGE + ".api", FRAMEWORK_PACKAGE + ".common", FRAMEWORK_PACKAGE + ".spi" }; private final Map<String, String> nativeLibs; public BundleClassLoader(final List<URL> urls, final Map<String, String> nativeLibs, final ClassLoader parent) { super(urls.toArray(new URL[urls.size()]), parent); this.nativeLibs = newReadOnlyMap(nativeLibs); } /** * Overrides <code>super.getResource()</code>, to change loading model to * child-first and to restrict access to certain classes. */ @Override public URL getResource(String name) { URL url = findResource(name); if (url == null) { url = super.getResource(name); } return url; } /** * Overrides <code>super.loadClass()</code>, to change loading model to * child-first and to restrict access to certain classes. */ @Override public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException { Class<?> c = findLoadedClass(name); if (c == null) { try { //try to find it in the bundle's class loader //anything there is considered accessible c = findClass(name); } catch (ClassNotFoundException ex) { // Hack for OIM: the framework is loaded by OIM's tcADPClassLoader, whose // delegation strategy (parent first) is to first try to load // the class through Thread.currentThread().getContextClassLoader(). When the // framework is running a connector operation, the thread context class loader is // BundleClassLoader. Without the hack, BundleClassLoader would delegate to its parent // (i.e., tcADPClassLoader), which would again delegate to the thread context class loader // (i.e., BundleClassLoader), resulting in an infinite loop reported by the JVM through a ClassCircularityError. // The hack sets the thread context class loader to its initial value when // BundleClassLoader delegates to its parent. if (runningInOIM()) { final List<ClassLoader> loaders = ThreadClassLoaderManager.getInstance().popAll(); try { // check parents class loader c = getParent().loadClass(name); // We cannot check the allowed packages; because of the OIM bug, // BundleClassLoader may be asked to load framework-internal classes too. } finally { ThreadClassLoaderManager.getInstance().pushAll(loaders); } } else { // check parents class loader c = getParent().loadClass(name); //make sure it's only in set of allowed packages checkAccessAllowed(c); } } } if (resolve) { resolveClass(c); } return c; } private boolean runningInOIM() { final ClassLoader loader = this.getClass().getClassLoader(); return loader != null && loader.getClass().getName().contains("tcADPClassLoader"); } private void checkAccessAllowed(final Class<?> c) throws ClassNotFoundException { final String name = c.getName(); if (!name.startsWith(FRAMEWORK_PACKAGE + ".")) { return; } for (String pack : ALLOWED_FRAMEWORK_PACKAGES) { if (name.startsWith(pack + ".")) { return; } } String message = "Connector may not reference class '"+name+"', "+ "it is an internal framework class."; throw new ClassNotFoundException(message); } @Override protected String findLibrary(final String libname) { return nativeLibs.get(System.mapLibraryName(libname)); } }