/* * ==================== * 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 2013 ConnId */ package org.identityconnectors.framework.impl.api.local; import java.util.ArrayList; import java.util.List; import java.util.Stack; import org.identityconnectors.common.logging.Log; /** * Provides a for managing the thread-local class loader * */ public final class ThreadClassLoaderManager { private static final Log LOG = Log.getLog(ThreadClassLoaderManager.class); private static final ThreadLocal<ThreadClassLoaderManager> INSTANCE = new ThreadLocal<ThreadClassLoaderManager>() { @Override public ThreadClassLoaderManager initialValue() { return new ThreadClassLoaderManager(); } }; private final Stack<ClassLoader> loaderStack = new Stack<ClassLoader>(); private ThreadClassLoaderManager() { // empty constructor for singleton class } /** * Returns the thread-local instance of the manager * * @return the thread-local instance of the manager */ public static ThreadClassLoaderManager getInstance() { return INSTANCE.get(); } /** * Clear the thread-local instance of the manager. */ public static void clearInstance() { INSTANCE.remove(); } /** * Sets the given loader as the thread-local classloader. * * @param loader * The class loader. May be null. */ public void pushClassLoader(final ClassLoader loader) { loaderStack.push(getCurrentClassLoader()); Thread.currentThread().setContextClassLoader(loader); } /** * Restores the previous loader as the thread-local classloader. */ public void popClassLoader() { if (loaderStack.isEmpty()) { throw new IllegalStateException("Stack size is 0"); } final ClassLoader previous = loaderStack.pop(); Thread.currentThread().setContextClassLoader(previous); } /** * Hack for OIM. See BundleClassLoader. Pops and returns all class loaders * previously pushed, therefore effectively setting the thread's current * context class loader to the initial class loader. */ public List<ClassLoader> popAll() { final List<ClassLoader> rv = new ArrayList<ClassLoader>(loaderStack); while (!loaderStack.isEmpty()) { popClassLoader(); } return rv; } /** * Hack for OIM. See BundleClassLoader. Pushes all class loaders in the list * as the context class loader. * * @param loaders * the loaders to push; never null. */ public void pushAll(final List<ClassLoader> loaders) { for (ClassLoader loader : loaders) { pushClassLoader(loader); } } /** * Returns the current thread-local class loader * * @return the current thread-local class loader */ public ClassLoader getCurrentClassLoader() { final ClassLoader result = Thread.currentThread().getContextClassLoader(); // Attempt to provide more information to issue 604. if (result == null) { LOG.error(new Throwable(), "The CCL of current thread ''{0}'' is null", Thread .currentThread().getName()); } return result; } }