Package org.jboss.ejb3.singleton.deployer

Source Code of org.jboss.ejb3.singleton.deployer.SingletonContainerDeployer

/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb3.singleton.deployer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;

import org.jboss.aop.AspectManager;
import org.jboss.aop.Domain;
import org.jboss.aop.DomainDefinition;
import org.jboss.beans.metadata.api.annotations.Inject;
import org.jboss.beans.metadata.plugins.AbstractInjectionValueMetaData;
import org.jboss.beans.metadata.spi.BeanMetaData;
import org.jboss.beans.metadata.spi.DemandMetaData;
import org.jboss.beans.metadata.spi.DependencyMetaData;
import org.jboss.beans.metadata.spi.SupplyMetaData;
import org.jboss.beans.metadata.spi.builder.BeanMetaDataBuilder;
import org.jboss.deployers.spi.DeploymentException;
import org.jboss.deployers.spi.deployer.helpers.AbstractRealDeployerWithInput;
import org.jboss.deployers.spi.deployer.helpers.DeploymentVisitor;
import org.jboss.deployers.structure.spi.DeploymentUnit;
import org.jboss.ejb3.Container;
import org.jboss.ejb3.DependencyPolicy;
import org.jboss.ejb3.Ejb3Registry;
import org.jboss.ejb3.MCDependencyPolicy;
import org.jboss.ejb3.common.deployers.spi.AttachmentNames;
import org.jboss.ejb3.common.resolvers.spi.EjbReferenceResolver;
import org.jboss.ejb3.container.spi.EJBContainer;
import org.jboss.ejb3.kernel.JNDIKernelRegistryPlugin;
import org.jboss.ejb3.resolvers.MessageDestinationReferenceResolver;
import org.jboss.ejb3.singleton.aop.impl.AOPBasedSingletonContainer;
import org.jboss.ejb3.singleton.impl.resolver.EjbLinkResolver;
import org.jboss.jpa.resolvers.PersistenceUnitDependencyResolver;
import org.jboss.logging.Logger;
import org.jboss.metadata.ejb.jboss.JBossEnterpriseBeanMetaData;
import org.jboss.metadata.ejb.jboss.JBossSessionBean31MetaData;
import org.jboss.metadata.ejb.jboss.jndi.resolver.impl.JNDIPolicyBasedJNDINameResolverFactory;
import org.jboss.metadata.ejb.jboss.jndi.resolver.spi.SessionBean31JNDINameResolver;
import org.jboss.metadata.ejb.jboss.jndipolicy.plugins.DefaultJNDIBindingPolicyFactory;
import org.jboss.metadata.ejb.jboss.jndipolicy.spi.DefaultJndiBindingPolicy;
import org.jboss.metadata.ejb.spec.BusinessLocalsMetaData;
import org.jboss.metadata.ejb.spec.BusinessRemotesMetaData;
import org.jboss.reloaded.naming.deployers.javaee.JavaEEComponentInformer;
import org.jboss.reloaded.naming.spi.JavaEEComponent;

/**
* A MC based deployer for deploying a {@link EJBContainer} as a MC bean
* for each singleton bean.
* <p>
*  Expects {@link JBossEnterpriseBeanMetaData} as an input and processes the metadata
*  for any potential singleton beans. If any singleton bean is found, then this deployer
*  creates a {@link BeanMetaData} for a container corresponding to the singleton
*  bean. The {@link BeanMetaData} is then attached to the {@link DeploymentUnit} so that MC
*  can deploy it as MC bean.
* </p>
* <p>
* Before deploying the container as a MC bean, this deployer sets up appropriate dependencies for
* the container.
* </p>
*
* @author Jaikiran Pai
* @version $Revision: $
*/
public class SingletonContainerDeployer extends AbstractRealDeployerWithInput<JBossEnterpriseBeanMetaData>
      implements
         DeploymentVisitor<JBossEnterpriseBeanMetaData>
{

   /**
    * Logger
    */
   private static Logger logger = Logger.getLogger(SingletonContainerDeployer.class);

   /**
    * Message destination resolver to be set in the container, which this
    * deployer creates
    */
   private MessageDestinationReferenceResolver messageDestinationResolver;

   /**
    * Ejb reference resolver to be set in the container, which this
    * deployer creates
    */
   private EjbReferenceResolver ejbReferenceResolver;

   /**
    * Persistence unit resolver to be set in the container, which this
    * deployer creates
    */
   private PersistenceUnitDependencyResolver puResolver;

   /**
    * component informer
    */
   private JavaEEComponentInformer javaeeComponentInformer;

   /**
    * Stores the reference of singleton containers which have been deployed, by this deployer.
    * <p>
    *   This information will be used during undeployment of the deployment unit
    * </p>
    * <p>
    *   The key of this {@link Map} is the name of the container and the value is the container itself.
    * </p>
    */
   protected Map<String, Container> singletonContainers = new HashMap<String, Container>();

   /**
    * Constructs a {@link SingletonContainerDeployer} for
    * processing singleton beans
    */
   public SingletonContainerDeployer()//JavaEEComponentInformer javaCompInformer)
   {
      //setInput(JBossMetaData.class);
      //this.javaeeComponentInformer = javaCompInformer;
      //      Set<String> inputs = new HashSet<String>();
      //      inputs.add(JBossEnterpriseBeanMetaData.class.getName());
      //      List<String> javaCompRequiredAttachments = Arrays.asList(this.javaeeComponentInformer.getRequiredAttachments());
      //      inputs.addAll(javaCompRequiredAttachments);
      //     
      //      this.setInputs(inputs);
      this.setDeploymentVisitor(this);
      this.setInput(JBossEnterpriseBeanMetaData.class);
      this.setComponentsOnly(true);
      // setStage(DeploymentStages.REAL);

      // we output the container as a MC bean
      addOutput(BeanMetaData.class);
      addOutput(org.jboss.ejb3.EJBContainer.class);

      // ordering
      // new SPI based EJB3Deployment
      //addInput(EJB3Deployment.class);
      addInput(AttachmentNames.PROCESSED_METADATA);
      addInput(org.jboss.ejb3.async.spi.AttachmentNames.ASYNC_INVOCATION_PROCESSOR);
   }

   /**
    * Processes a {@link DeploymentUnit} with {@link JBossEnterpriseBeanMetaData} to
    * check if it's a singleton bean.
    * <p>
    *  If it's a singleton bean then this method creates a {@link EJBContainer}, for that singleton bean and
    *  deploys it as a MC bean
    * </p>
    *
    */
   @Override
   public void deploy(DeploymentUnit unit, JBossEnterpriseBeanMetaData beanMetaData) throws DeploymentException
   {
      if (!isSingletonBean(beanMetaData))
      {
         return;
      }

      ExecutorService asyncExecutorService = (ExecutorService) unit.getAttachment(org.jboss.ejb3.async.spi.AttachmentNames.ASYNC_INVOCATION_PROCESSOR);
      if (asyncExecutorService == null)
      {
         throw new IllegalStateException("No async executor available for deployment unit " + unit);
      }
      // now start with actual processing
      JBossSessionBean31MetaData sessionBean = (JBossSessionBean31MetaData) beanMetaData;

      // Create a singleton container
      ClassLoader classLoader = unit.getClassLoader();
      String domainName = AOPBasedSingletonContainer.getAOPDomainName();
      DomainDefinition singletonContainerAOPDomain = AspectManager.instance().getContainer(domainName);
      if (singletonContainerAOPDomain == null)
      {
         throw new DeploymentException(domainName + " AOP domain not configured - cannot deploy EJB named "
               + beanMetaData.getEjbName() + " in unit " + unit);
      }
      Hashtable<String, String> ctxProperties = new Hashtable<String, String>();
      AOPBasedSingletonContainer singletonContainer;
      try
      {
         singletonContainer = new AOPBasedSingletonContainer(classLoader, sessionBean.getEjbClass(), sessionBean
               .getEjbName(), (Domain) singletonContainerAOPDomain.getManager(), ctxProperties, sessionBean, unit, asyncExecutorService);
      }
      catch (ClassNotFoundException cnfe)
      {
         throw new DeploymentException(cnfe);
      }

      singletonContainer.setEjbReferenceResolver(this.ejbReferenceResolver);
      singletonContainer.setMessageDestinationResolver(this.messageDestinationResolver);
      singletonContainer.setPersistenceUnitResolver(this.puResolver);

      singletonContainer.instantiated();

      singletonContainer.processMetadata();
      // register the container with Ejb3Registry and also store reference locally (for use during
      // undeploy)
      this.registerContainer(sessionBean.getContainerName(), singletonContainer);

      // attach the container to the deployment unit, with appropriate MC dependencies
      this.installContainer(unit, singletonContainer.getObjectName().getCanonicalName(), singletonContainer);

   }

   /**
    * @see org.jboss.deployers.spi.deployer.helpers.AbstractDeployer#undeploy(org.jboss.deployers.structure.spi.DeploymentUnit)
    */
   @Override
   public void undeploy(DeploymentUnit unit, JBossEnterpriseBeanMetaData enterpriseBean)
   {
      if (isSingletonBean(enterpriseBean))
      {
         String containerName = enterpriseBean.getContainerName();
         this.unregisterContainer(containerName);

      }
   }

   /**
    * @see org.jboss.deployers.spi.deployer.helpers.DeploymentVisitor#getVisitorType()
    */
   @Override
   public Class<JBossEnterpriseBeanMetaData> getVisitorType()
   {
      return JBossEnterpriseBeanMetaData.class;
   }

   /**
    * Returns true if the passed <code>beanMetaData</code> corresponds to a Singleton bean.
    * Else returns false.
    *
    * @param beanMetaData The bean metadata
    * @return
    */
   private boolean isSingletonBean(JBossEnterpriseBeanMetaData beanMetaData)
   {
      // we are only interested in EJB3.1
      if (!beanMetaData.getJBossMetaData().isEJB31())
      {
         if (logger.isTraceEnabled())
         {
            logger.trace("Not a EJB3.1 bean " + beanMetaData.getName());
         }
         return false;
      }
      // we are not interested in non-session beans
      if (!beanMetaData.isSession())
      {
         if (logger.isTraceEnabled())
         {
            logger.trace("Not a session bean " + beanMetaData.getName());
         }
         return false;
      }
      // one last check to make sure we have got the right type!
      if (!(beanMetaData instanceof JBossSessionBean31MetaData))
      {
         if (logger.isTraceEnabled())
         {
            logger.trace("Session bean " + beanMetaData.getName() + " is not of type "
                  + JBossSessionBean31MetaData.class);
         }
         return false;
      }

      // now start with actual processing
      JBossSessionBean31MetaData sessionBean = (JBossSessionBean31MetaData) beanMetaData;
      // we are only concerned with Singleton beans
      if (!sessionBean.isSingleton())
      {
         if (logger.isTraceEnabled())
         {
            logger.trace("Not a singleton bean " + sessionBean.getName());
         }
         return false;
      }
      // it's a singleton bean
      return true;
   }

   /**
    * Register the container with the {@link Ejb3Registry} and also store a reference to the container
    * locally in {@link #singletonContainers}
    *
    * @param containerName The container name which will be used to store the container in {@link #singletonContainers}.
    *                       Should *not* be null
    * @param container The container being registered. Should *not* be null
    */
   private void registerContainer(String containerName, Container container)
   {
      // org.jboss.ejb3.remoting.IsLocalInterceptor requires the container to be registered with Ejb3Registry
      Ejb3Registry.register(container);
      // we need to cleanup in undeploy(), so keep an reference of containers which we registered
      this.singletonContainers.put(containerName, container);
   }

   /**
    * Unregisters the container with name <code>containerName</code>, from the
    * {@link Ejb3Registry} as well as removes the reference from {@link #singletonContainers}
    *
    * @param containerName The name of the container
    */
   private void unregisterContainer(String containerName)
   {
      // remove (any) locally stored reference to container
      Container container = this.singletonContainers.remove(containerName);
      // unregister from Ejb3Registry
      if (container != null)
      {
         Ejb3Registry.unregister(container);
      }
   }

   /**
    * Creates a  MC bean for the <code>container</code> and adds it as a {@link BeanMetaData} to the unit
    * <p>
    *   Appropriate dependencies identified through the {@link org.jboss.ejb3.EJBContainer#getDependencyPolicy()}
    *   are set on the container MC bean.
    * </p>
    * <p>
    *   This method also processes any depends-on present on the singleton bean and adds the appropriate dependencies
    *   to the container MC bean. When a singleton bean depends on the other singleton bean, we add the following dependencies
    *   to the container MC bean:
    *   <ul>
    *       <li>  A dependency on the target EJB container </li>
    *        <li> A dependency on each of the exposed JNDI names of the target EJB (so that the target EJB can be
    *               accessed through JNDI within the dependent EJB)</li>
    *   </ul>           
    *
    * @param unit The deployment unit
    * @param containerMCBeanName The MC bean name of the container
    * @param container The container being installed
    */
   private void installContainer(DeploymentUnit unit, String containerMCBeanName, AOPBasedSingletonContainer container)
   {
      BeanMetaDataBuilder containerBMDBuilder = BeanMetaDataBuilder.createBuilder(containerMCBeanName, container
            .getClass().getName());
      containerBMDBuilder.setConstructorValue(container);

      DependencyPolicy containerDependencyPolicy = container.getDependencyPolicy();

      // Process @DependsOn/depends-on
      // Add any dependencies based on @DependsOn/depends-on elements
      JBossSessionBean31MetaData sessionBeanMetaData = (JBossSessionBean31MetaData) container.getMetaData();
      String[] dependsOn = sessionBeanMetaData.getDependsOn();
      if (dependsOn != null)
      {
         EjbLinkResolver ejbLinkResolver = new EjbLinkResolver();
         for (String dependency : dependsOn)
         {
            // resolve the EJB through the ejb link in depends-on
            JBossEnterpriseBeanMetaData dependencyBean = ejbLinkResolver.resolveEJB(dependency, unit);
            if (dependencyBean == null)
            {
               throw new RuntimeException("Could not resolve bean for @DependsOn/depends-on with ejb-name: "
                     + dependency + " while processing EJB named " + container.getEJBName());
            }
            if (isSingletonBean(dependencyBean) == false)
            {
               throw new RuntimeException("@DependsOn/depends-on can only refer to Singleton beans. "
                     + dependencyBean.getEjbClass() + " is not a singleton bean");
            }
            // when a singleton bean depends on the other singleton bean, we add:
            // 1) A dependency on the target EJB container
            // 2) A dependency on each of the exposed JNDI names of the target EJB (so that the
            // target EJB can be accessed through JNDI within the dependent EJB)
           
            // get the exposed JNDI names
            List<String> jndiNames = this.getExposedJNDINames((JBossSessionBean31MetaData) dependencyBean);
            for (String jndiName : jndiNames)
            {
               // add each jndi name as a dependency
               // Note: The dependency resolution of a dependency with prefix JNDIKernelRegistryPlugin.JNDI_DEPENDENCY_PREFIX
               // is handled by JNDIKernelRegistryPlugin, which does a JNDI lookup to see if the dependency is resolved.
               // Effectively, none of the MC beans have to add the jndi name as an explicit supply. So when the
               // corresponding jndi binder binds this jndi name to JNDI tree, the dependency will be marked as resolved
               containerDependencyPolicy.addDependency(JNDIKernelRegistryPlugin.JNDI_DEPENDENCY_PREFIX + jndiName);
            }
            String dependencyBeanContainerName = dependencyBean.getContainerName();
            containerDependencyPolicy.addDependency(dependencyBeanContainerName);

         }
      }

      logger.debug("Installing container for EJB " + container.getEJBName());
      if (containerDependencyPolicy instanceof MCDependencyPolicy)
      {
         MCDependencyPolicy policy = (MCDependencyPolicy) containerDependencyPolicy;
         // depends
         Set<DependencyMetaData> dependencies = policy.getDependencies();
         if (dependencies != null && dependencies.isEmpty() == false)
         {
            logger.debug("with dependencies: ");
            for (DependencyMetaData dependency : dependencies)
            {
               logger.debug(dependency.getDependency());
               containerBMDBuilder.addDependency(dependency.getDependency());
            }
         }
         // demands
         Set<DemandMetaData> demands = policy.getDemands();
         if (demands != null && demands.isEmpty() == false)
         {
            logger.debug("with demands: ");
            for (DemandMetaData demand : demands)
            {
               logger.debug(demand.getDemand());
               containerBMDBuilder.addDemand(demand.getDemand());
            }
         }
         // supplies
         Set<SupplyMetaData> supplies = policy.getSupplies();
         if (supplies != null && supplies.isEmpty() == false)
         {
            logger.debug("with supplies: ");
            for (SupplyMetaData supply : supplies)
            {
               logger.debug(supply.getSupply());
               containerBMDBuilder.addSupply(supply.getSupply());
            }
         }
      }

      // Add inject metadata on container
      String javaCompMCBeanName = this.getJavaEEComponentMCBeanName(unit);
      AbstractInjectionValueMetaData javaCompInjectMetaData = new AbstractInjectionValueMetaData(javaCompMCBeanName);
      // Too bad we have to know the field name. Need to do more research on MC to see if we can
      // add property metadata based on type instead of field name.
      containerBMDBuilder.addPropertyMetaData("javaComp", javaCompInjectMetaData);

      // TODO: This is an undocumented nonsense of MC
      DeploymentUnit parentUnit = unit.getParent();
      parentUnit.addAttachment(BeanMetaData.class + ":" + containerMCBeanName, containerBMDBuilder.getBeanMetaData());
      unit.addAttachment(org.jboss.ejb3.EJBContainer.class + ":" + containerMCBeanName, container);
   }

   /**
    * Returns the {@link JavaEEComponent} MC bean name
    * @param deploymentUnit
    * @return
    */
   private String getJavaEEComponentMCBeanName(DeploymentUnit deploymentUnit)
   {
      String applicationName = this.javaeeComponentInformer.getApplicationName(deploymentUnit);
      String moduleName = this.javaeeComponentInformer.getModulePath(deploymentUnit);
      String componentName = this.javaeeComponentInformer.getComponentName(deploymentUnit);

      final StringBuilder builder = new StringBuilder("jboss.naming:");
      if (applicationName != null)
      {
         builder.append("application=").append(applicationName).append(",");
      }
      builder.append("module=").append(moduleName);
      if (componentName != null)
      {
         builder.append(",component=").append(componentName);
      }
      return builder.toString();
   }

   @Inject
   public void setPersistenceUnitResolver(PersistenceUnitDependencyResolver puResolver)
   {
      this.puResolver = puResolver;
   }

   @Inject
   public void setMessageDestinationResolver(MessageDestinationReferenceResolver messageDestResolver)
   {
      this.messageDestinationResolver = messageDestResolver;
   }

   @Inject
   public void setEjbRefResolver(EjbReferenceResolver ejbRefResolver)
   {
      this.ejbReferenceResolver = ejbRefResolver;
   }

   @Inject
   public void setJavaEEComponentInformer(JavaEEComponentInformer componentInformer)
   {
      this.javaeeComponentInformer = componentInformer;
   }

   /**
    * Returns the JNDI names that are exposed by this <code>sessionBean</code>.
    * Returns an empty list if there are no JNDI names exposed by this session bean.
    * <p>
    *   This method checks whether the bean exposes a remote view, a local view and/or a nointerface view.
    *   Depending on what views the bean exposes, the returned list will contain either or all of the
    *   following JNDI names:
    *   <ul>
    *       <li> default local business interface JNDI name</li>
    *       <li> default remote business interface JNDI name</li>
    *       <li> nointerface view JNDI name</li>
    *   </ul>
    *      
    * </p>
    * @param sessionBean The session bean.
    * @return
    * TODO: Should we even consider business interface specific JNDI names? Will the dependent bean clients
    * expect use these jndi names for lookup?
    */
   private List<String> getExposedJNDINames(JBossSessionBean31MetaData sessionBean)
   {
      List<String> jndiNames = new ArrayList<String>();
   
      DefaultJndiBindingPolicy jndiPolicy = DefaultJNDIBindingPolicyFactory.getDefaultJNDIBindingPolicy();
      SessionBean31JNDINameResolver jndiNameResolver = JNDIPolicyBasedJNDINameResolverFactory.getJNDINameResolver(
            sessionBean, jndiPolicy);
     
      // Determine if there are local/remote views
      BusinessRemotesMetaData businessRemotes = sessionBean.getBusinessRemotes();
      BusinessLocalsMetaData businessLocals = sessionBean.getBusinessLocals();
     
      boolean hasLocalBusinessView = (businessLocals != null && businessLocals.size() > 0);
      boolean hasRemoteBusinessView = (businessRemotes != null && businessRemotes.size() > 0);
     
      // It's got a local business view, so resolve the default local business JNDI name
      // and add it to the list
      if (hasLocalBusinessView)
      {
         String defaultLocalJNDIName = jndiNameResolver.resolveLocalBusinessDefaultJNDIName(sessionBean);
         if (defaultLocalJNDIName != null)
         {
            jndiNames.add(defaultLocalJNDIName);
         }
      }
     
      // It's got a remote business view, so resolve the default remote business JNDI name
      // and add it to the list
      if (hasRemoteBusinessView)
      {
         String defaultRemoteJNDIName = jndiNameResolver.resolveRemoteBusinessDefaultJNDIName(sessionBean);
         if (defaultRemoteJNDIName != null)
         {
            jndiNames.add(defaultRemoteJNDIName);
         }
      }
     
      // It's got a nointerface view, so resolve the jndi name and add it to the list
      if (sessionBean.isNoInterfaceBean())
      {
         String noInterfaceJNDIName = jndiNameResolver.resolveNoInterfaceJNDIName(sessionBean);
         if (noInterfaceJNDIName != null)
         {
            jndiNames.add(noInterfaceJNDIName);
         }

      }
      // return the exposed JNDI names
      return jndiNames;
   }
}
TOP

Related Classes of org.jboss.ejb3.singleton.deployer.SingletonContainerDeployer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.