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

org.apache.deltaspike.core.api.provider.BeanManagerProvider Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 org.apache.deltaspike.core.api.provider;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeShutdown;
import javax.enterprise.inject.spi.Extension;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import org.apache.deltaspike.core.util.ClassUtils;


/**
 * 

This class provides access to the {@link BeanManager} * by registering the current {@link BeanManager} in an extension and * making it available via a singleton factory for the current application.

*

This is really handy if you like to access CDI functionality * from places where no injection is available.

*

If a simple but manual bean-lookup is needed, it's easier to use the {@link BeanProvider}.

*

*

As soon as an application shuts down, the reference to the {@link BeanManager} will be removed.

*

*

Usage:

*

 * BeanManager bm = BeanManagerProvider.getInstance().getBeanManager();
 * 
 * 
* *

Attention: This method is intended for being used in user code at runtime. * If this method gets used during Container boot (in an Extension), non-portable * behaviour results. During bootstrapping an Extension shall @Inject BeanManager to get * access to the underlying BeanManager (see e.g. {@link #cleanupFinalBeanManagers} ). * This is the only way to guarantee to get the right * BeanManager in more complex Container scenarios.

*/ public class BeanManagerProvider implements Extension { private static final Logger LOG = Logger.getLogger(BeanManagerProvider.class.getName()); private static BeanManagerProvider bmpSingleton = null; /** * This data container is used for storing the BeanManager for each * WebApplication. This is needed in EAR or other multi-webapp scenarios * if the DeltaSpike classes (jars) are provided in a shared ClassLoader. */ private static class BeanManagerInfo { /** * The BeanManager picked up via Extension loading */ private BeanManager loadTimeBm = null; /** * The final BeanManagers. * After the container did finally boot, we first try to resolve them from JNDI, * and only if we don't find any BM there we take the ones picked up at startup. */ private BeanManager finalBm = null; /** * Whether the CDI Application has finally booted. * Please note that this is only a nearby value * as there is no reliable event for this status in EE6. */ private boolean booted = false; } /** *

The BeanManagerInfo for the current ClassLoader.

*

Attention: This instance must only be used through the {@link #bmpSingleton} singleton!

*/ private volatile Map bmInfos = new ConcurrentHashMap(); /** * Returns if the {@link BeanManagerProvider} has been initialized. * Usually it isn't needed to call this method in application code. * It's e.g. useful for other frameworks to check if DeltaSpike and the CDI container in general have been started. * * @return true if the bean-manager-provider is ready to be used */ public static boolean isActive() { return bmpSingleton != null; } /** * Allows to get the current provider instance which provides access to the current {@link BeanManager} * * @throws IllegalStateException if the {@link BeanManagerProvider} isn't ready to be used. * That's the case if the environment isn't configured properly and therefore the {@link AfterBeanDiscovery} * hasn't be called before this method gets called. * @return the singleton BeanManagerProvider */ public static BeanManagerProvider getInstance() { /*X TODO Java-EE5 support needs to be discussed if (bmpSingleton == null) { // workaround for some Java-EE5 environments in combination with a special // StartupBroadcaster for bootstrapping CDI // CodiStartupBroadcaster.broadcastStartup(); // here bmp might not be null (depends on the broadcasters) } */ if (bmpSingleton == null) { throw new IllegalStateException("No " + BeanManagerProvider.class.getName() + " in place! " + "Please ensure that you configured the CDI implementation of your choice properly. " + "If your setup is correct, please clear all caches and compiled artifacts."); } return bmpSingleton; } /** * It basically doesn't matter which of the system events we use, * but basically we use the {@link AfterBeanDiscovery} event since it allows to use the * {@link BeanManagerProvider} for all events which occur after the {@link AfterBeanDiscovery} event. * * @param afterBeanDiscovery event which we don't actually use ;) * @param beanManager the BeanManager we store and make available. */ public void setBeanManager(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) { setBeanManagerProvider(this); BeanManagerInfo bmi = getBeanManagerInfo(ClassUtils.getClassLoader(null)); bmi.loadTimeBm = beanManager; } /** * The active {@link BeanManager} for the current application (/{@link ClassLoader}). This method will throw an * {@link IllegalStateException} if the BeanManager cannot be found. * * @return the current bean-manager, never null * @throws IllegalStateException if the BeanManager cannot be found */ public BeanManager getBeanManager() { BeanManagerInfo bmi = getBeanManagerInfo(ClassUtils.getClassLoader(null)); // warn the user if he tries to use the BeanManager before container startup if (!bmi.booted) { if (!isParentBeanManagerBooted()) { LOG.warning("When using the BeanManager to retrieve Beans before the Container is started," + " non-portable behaviour results!"); // reset the flag to only issue the warning once. // this is a workaround for some containers which mess up EAR handling. bmi.booted = true; } } BeanManager result = bmi.finalBm; if (result == null) { synchronized (bmi) { result = bmi.finalBm; if (result == null) { // first we look for a BeanManager from JNDI result = resolveBeanManagerViaJndi(); if (result == null) { // if none found, we take the one we got from the Extension loading result = bmi.loadTimeBm; } if (result == null) { throw new IllegalStateException("Unable to find BeanManager. " + "Please ensure that you configured the CDI implementation of your choice properly."); } // store the resolved BeanManager in the result cache until #cleanupFinalBeanManagers gets called // -> afterwards the next call of #getBeanManager will trigger the final lookup bmi.finalBm = result; } } } return result; } /** * By cleaning the final BeanManager map after the Deployment got Validated, * we prevent premature loading of information from JNDI in cases where the * container might not be fully setup yet. * * This might happen if someone uses the BeanManagerProvider during Extension * startup. This should generally avoided but instead you should just use * an injected BeanManager in your Extension and propagate the BeanManager * via setters. * * In EARs with multiple webapps you might get different Extensions per WAR. * This depends on the container you use. By resetting all known * BeanManagerInfos we try to */ public void cleanupFinalBeanManagers(@Observes AfterDeploymentValidation adv) { for (BeanManagerInfo bmi : bmpSingleton.bmInfos.values()) { bmi.finalBm = null; bmi.booted = true; /*possible issue with >weld< based servers: if #getBeanManager gets called in a custom AfterDeploymentValidation observer >after< this observer, the wrong bean-manager might get stored (not deterministic due to the unspecified order of observers). finally a bean-manager for a single bda will be stored and returned (which isn't the bm exposed via jndi).*/ } } /** * Cleanup on container shutdown * * @param beforeShutdown cdi shutdown event */ public void cleanupStoredBeanManagerOnShutdown(@Observes BeforeShutdown beforeShutdown) { if (bmpSingleton == null) { // this happens if there has been a failure at startup return; } ClassLoader classLoader = ClassUtils.getClassLoader(null); bmpSingleton.bmInfos.remove(classLoader); } /** * Get the BeanManager from the JNDI registry. * * @return current {@link BeanManager} which is provided via JNDI */ private BeanManager resolveBeanManagerViaJndi() { try { // this location is specified in JSR-299 and must be // supported in all certified EE environments return (BeanManager) new InitialContext().lookup("java:comp/BeanManager"); } catch (NamingException e) { //workaround didn't work -> return null return null; } } /** * Get or create the BeanManagerInfo for the given ClassLoader */ private BeanManagerInfo getBeanManagerInfo(ClassLoader cl) { BeanManagerInfo bmi = bmpSingleton.bmInfos.get(cl); if (bmi == null) { synchronized (this) { bmi = bmpSingleton.bmInfos.get(cl); if (bmi == null) { bmi = new BeanManagerInfo(); bmpSingleton.bmInfos.put(cl, bmi); } } } return bmi; } /** * This function exists to prevent findbugs to complain about * setting a static member from a non-static function. * * @param beanManagerProvider the bean-manager-provider which should be used if there isn't an existing provider * @return the first BeanManagerProvider */ private static BeanManagerProvider setBeanManagerProvider(BeanManagerProvider beanManagerProvider) { if (bmpSingleton == null) { bmpSingleton = beanManagerProvider; } return bmpSingleton; } /** * @return whether a BeanManagerInfo for a parent classloader is available and has the booted flag set. */ private boolean isParentBeanManagerBooted() { ClassLoader classLoader = ClassUtils.getClassLoader(null); BeanManagerInfo parentBmi = getParentBeanManagerInfo(classLoader); return parentBmi != null && parentBmi.booted; } /** * This method recurses into the parent ClassLoaders and will check if a * BeanManagerInfo for it exists. * @return the BeanManagerInfo of the parent ClassLoader hierarchy if any exists, * or null if there is no {@link BeanManagerInfo} for the ClassLoaders in the hierarchy. */ private BeanManagerInfo getParentBeanManagerInfo(ClassLoader classLoader) { ClassLoader parentClassLoader = classLoader.getParent(); if (parentClassLoader.equals(ClassLoader.getSystemClassLoader())) { return null; } BeanManagerInfo bmi = getBeanManagerInfo(parentClassLoader); if (bmi == null) { bmi = getParentBeanManagerInfo(parentClassLoader); } return bmi; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy