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

org.glassfish.weld.ACLSingletonProvider Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2021, 2024 Contributors to Eclipse Foundation.
 * Copyright (c) 2009, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.weld;

import java.lang.System.Logger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.glassfish.internal.api.ClassLoaderHierarchy;
import org.glassfish.internal.api.Globals;
import org.glassfish.javaee.full.deployment.EarLibClassLoader;
import org.glassfish.web.loader.WebappClassLoader;
import org.jboss.weld.bootstrap.api.Singleton;
import org.jboss.weld.bootstrap.api.SingletonProvider;
import org.jboss.weld.bootstrap.api.helpers.TCCLSingletonProvider;

import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.TRACE;
import static java.lang.Thread.currentThread;

/**
 * Singleton provider that uses Application ClassLoader to differentiate between applications.
 * 

* It is different from {@link org.jboss.weld.bootstrap.api.helpers.TCCLSingletonProvider}. *

* We can't use TCCLSingletonProvider because thread's context class loader can be different for * different modules of a single application (ear). * To support Application Scoped beans, Weld needs to be bootstrapped per application as * opposed to per module. We rely on the fact that all these module class loaders have a common * parent which is per application. We use that parent ApplicationClassLoader to identify the * singleton scope. *

* This class assumes a certain delegation hierarchy of application class loaders. So, deployment * team should be aware of this class and change it if application class loader hierarchy changes. * * @author [email protected] */ public class ACLSingletonProvider extends SingletonProvider { private static final Logger LOG = System.getLogger(ACLSingletonProvider.class.getName()); /** * Calls {@link SingletonProvider#initialize(SingletonProvider)} with * {@link ACLSingletonProvider} if there is an EAR support or with {@link TCCLSingletonProvider} * if EARs are not supported. */ public static void initializeSingletonProvider() { boolean earSupport; try { Class.forName("org.glassfish.javaee.full.deployment.EarClassLoader"); earSupport = true; } catch (ClassNotFoundException ignore) { earSupport = false; } SingletonProvider provider = earSupport ? new ACLSingletonProvider() : new TCCLSingletonProvider(); SingletonProvider.initialize(provider); LOG.log(DEBUG, () -> "SingletonProvider initialized: " + provider); } @Override public ACLSingleton create(Class expectedType) { return new ACLSingleton<>(); } private static class ACLSingleton implements Singleton { private static final Logger LOG = System.getLogger(ACLSingleton.class.getName()); private final Map store = new ConcurrentHashMap<>(); private final ClassLoader commonClassLoader = Globals.get(ClassLoaderHierarchy.class).getCommonClassLoader(); // Can't assume bootstrap loader as null. That's more of a convention, but some JVMs do not // use null for bootstap loader private static ClassLoader bootstrapCL; static { bootstrapCL = Object.class.getClassLoader(); } @Override public T get(String id) { ClassLoader appClassLoader = getClassLoader(); T instance = store.get(appClassLoader); if (instance == null) { throw new IllegalStateException("Singleton not set for " + appClassLoader); } LOG.log(DEBUG, () -> "get(" + id + ") - found " + instance + "; we use ear/war classloader instead of id."); return instance; } @Override public boolean isSet(String id) { return store.containsKey(getClassLoader()); } @Override public void set(String id, T object) { LOG.log(DEBUG, () -> "set(id=" + id + ", object=" + object + "); we use ear/war classloader instead of id."); store.put(getClassLoader(), object); } @Override public void clear(String id) { LOG.log(DEBUG, () -> "clear(id=" + id + "); we use ear/war classloader instead of id."); store.remove(getClassLoader()); } /** * This is the most significant method of this class. This is what distinguishes it from TCCLSIngleton. * *

* It tries to obtain a class loader that's common to all modules of an application (ear). Since it runs in the context * of Jakarta EE, it can assume that Thread's context class loader is always set as application class loader. In GlassFish, * the class loader can vary for each module of an Ear. Thread's context class loader is set depending on which module * is handling the request. * *

* But, fortunately all those embedded module class loaders have a common parent in their * delegation chain. That parent is of type EarLibClassLoader. So, this code walks up the delegation chain until it hits * either a EarLibClassLoader type of parent or bootstrapClassLoader. If former is the case, it returns that instance of * EarLibClassLoader. If latter is the case, it assumes that this is a standalone module and hence it returns the * thread's context class loader. * * @return a class loader that's common to all modules of a Jakarta EE application */ private ClassLoader getClassLoader() { ClassLoader contextClassLoader = currentThread().getContextClassLoader(); if (contextClassLoader == null) { throw new RuntimeException("Thread's context class loader is null"); } ClassLoader classLoader = contextClassLoader; ClassLoader appClassLoader = contextClassLoader; // Most of the time, the class loader of an application (whether it is a // standalone module or an ear) has a common class loader in their delegation chain. // // So, we can break the loop early for them. // // There are exceptions like hybrid application to this rule. // So, we have to walk up to bootstrapCL in worst case. while (classLoader != commonClassLoader && classLoader != bootstrapCL) { if (classLoader instanceof EarLibClassLoader) { return classLoader; } if (classLoader instanceof WebappClassLoader) { // We do this because it's possible for an app to change the thread's context class loader appClassLoader = classLoader; } classLoader = getParent(classLoader); } return appClassLoader; } private ClassLoader getParent(ClassLoader classLoader) { LOG.log(TRACE, () -> "getParent(classLoader=" + classLoader + ")"); return classLoader.getParent(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy