Package org.jboss.ejb3.proxy.factory

Source Code of org.jboss.ejb3.proxy.factory.BaseSessionProxyFactory

/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.proxy.factory;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.ejb.EJBException;
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import javax.ejb.EJBLocalObject;
import javax.ejb.EJBMetaData;
import javax.ejb.EJBObject;
import javax.ejb.Handle;
import javax.ejb.HomeHandle;
import javax.ejb.RemoteHome;
import javax.naming.NamingException;

import org.jboss.ejb3.EJBContainer;
import org.jboss.ejb3.Ejb3Registry;
import org.jboss.ejb3.SpecificationInterfaceType;
import org.jboss.ejb3.annotation.RemoteBinding;
import org.jboss.ejb3.proxy.JBossProxy;
import org.jboss.ejb3.proxy.handler.BaseSessionRemoteProxyInvocationHandler;
import org.jboss.ejb3.proxy.impl.EJBMetaDataImpl;
import org.jboss.ejb3.proxy.impl.HomeHandleImpl;
import org.jboss.ejb3.session.ProxyAccessType;
import org.jboss.ejb3.session.SessionContainer;
import org.jboss.ejb3.session.SessionSpecContainer;
import org.jboss.logging.Logger;
import org.jboss.metadata.ejb.jboss.JBossSessionBeanMetaData;
import org.jboss.metadata.ejb.jboss.jndipolicy.spi.JbossSessionBeanJndiNameResolver;
import org.jboss.metadata.ejb.spec.BusinessLocalsMetaData;
import org.jboss.metadata.ejb.spec.BusinessRemotesMetaData;
import org.jboss.util.naming.Util;

/**
* Comment
*
* @author <a href="mailto:bdecoste@jboss.com">William DeCoste</a>
* @version $Revision: 76905 $
*/
public abstract class BaseSessionProxyFactory implements SessionProxyFactory, Externalizable
{
   @SuppressWarnings("unused")
   private static final Logger log = Logger.getLogger(BaseSessionProxyFactory.class);
  
   private static final String JNDI_NAME_SUFFIX_PROXY_FACTORY = "/ProxyFactory";
  
   private SessionSpecContainer container;
   protected String containerGuid;
   protected String containerClusterUid;
   protected boolean isClustered = false;
   protected String jndiName;
  
   /**
    * Proxy Constructor for the Business Interfaces' Proxy
    */
   protected Constructor<?> businessProxyConstructor;
  
   /**
    * Proxy Constructor for the EJBObject/EJBLocalObject Proxy
    */
   protected Constructor<?> ejb21ProxyConstructor;
  
   private static final String METHOD_PREFIX_EJB21_CREATE = "create";
  
   public BaseSessionProxyFactory()
   {
   }
  
   protected BaseSessionProxyFactory(SessionSpecContainer container)
   {
      assert container != null : "container is null";
     
      setContainer(container);
   }
  
   public Object createHomeProxy()
   {
      throw new RuntimeException("NYI");
   }
  
   /**
    * Creates the Proxy constructors
    */
   protected void createProxyConstructors() throws Exception
   {
      // Obtain this bean class' CL
      ClassLoader cl = this.getContainer().getBeanClass().getClassLoader();
     
      // Create business proxy constructor
      Class<?>[] businessInterfaces = this.getInterfacesForBusinessProxy();
      this.businessProxyConstructor = ProxyFactoryHelper.createProxyConstructor(businessInterfaces, cl);
     
      // Create EJB21 proxy constructor
      Class<?>[] ejb21Interfaces = this.getInterfacesForEjb21Proxy();
      if (ejb21Interfaces != null)
      {
         this.ejb21ProxyConstructor = ProxyFactoryHelper.createProxyConstructor(ejb21Interfaces, cl);
      }
        
     
      /* plain jdk
      Class<?> proxyClass = java.lang.reflect.Proxy.getProxyClass(getContainer().getBeanClass().getClassLoader(), interfaces);
      final Class<?>[] constructorParams =
              {InvocationHandler.class};
      businessProxyConstructor = proxyClass.getConstructor(constructorParams);
     
      */
     
      /* javassist */
      /*
      proxyFactory = new javassist.util.proxy.ProxyFactory()
      {
         @Override
         protected ClassLoader getClassLoader()
         {
            return container.getBeanClass().getClassLoader();
         }
      };
      proxyFactory.setInterfaces(interfaces);
      proxyFactory.setSuperclass(JavassistProxy.class);
      proxyClass = proxyFactory.createClass();
      proxyConstructor = proxyClass.getConstructor((Class[]) null);
      */
     
      /* cglib */
      /*
      proxyClass = net.sf.cglib.proxy.Proxy.getProxyClass(container.getBeanClass().getClassLoader(), interfaces);
      final Class[] constructorParams = {net.sf.cglib.proxy.InvocationHandler.class};
      proxyConstructor = proxyClass.getConstructor(constructorParams);
      */
   }
  
   protected void bindProxy(Object proxy) throws NamingException
   {
      try
      {
         //TODO Dev
//         // Bind the Proxy Factory if not yet bound
//         try
//         {
//            // Check if bound yet
//            Util.lookup(this.getJndiNameProxyFactory(), this.getClass());
//         }
//         // Proxy factory is not yet bound
//         catch(NameNotFoundException nnfe)
//         {
//            // Bind Proxy Factory
//            log.debug("Binding proxy factory " + this.toString() + " for " + this.getContainer().getEjbName()
//                  + " in JNDI at " + this.getJndiNameProxyFactory());
//            Util.rebind(this.getContainer().getInitialContext(), this.getJndiNameProxyFactory(), this);
//         }
//         // Unexpected Exception
//         catch (Exception e)
//         {
//            throw new RuntimeException(e);
//         }
        
         // Bind the proxy itself
         log.debug("Binding proxy for " + getContainer().getEjbName() + " in JNDI at " + this.getJndiName());
         Util.rebind(getContainer().getInitialContext(), this.getJndiName(), proxy);
        
         // Bind a proxy per business interface
         //TODO This ugly block should be using polymorphism, but I'll allow it as the proxy mechanism
         // is going to be replaced entirely by EJB3 Proxy soon
         JBossSessionBeanMetaData smd = (JBossSessionBeanMetaData) container.getXml();
         BusinessRemotesMetaData remotes = smd.getBusinessRemotes();
         BusinessLocalsMetaData locals = smd.getBusinessLocals();
         Set<String> businessInterfaces = new HashSet<String>();
         boolean isLocal = this.isLocal();
         if (!isLocal)
         {
            if (remotes != null)
            {
               businessInterfaces.addAll(remotes);
            }
         }
         else
         {
            if (locals != null)
            {
               businessInterfaces.addAll(locals);
            }
         }
         for (String businessInterface : businessInterfaces)
         {
            String jndiName = JbossSessionBeanJndiNameResolver.resolveJndiName(smd, businessInterface);
            log.debug("Binding proxy for " + getContainer().getEjbName() + ", interface " + businessInterface
                  + " in JNDI at " + jndiName);
            if (Proxy.isProxyClass(proxy.getClass()))
            {
               for (Class<?> in : proxy.getClass().getInterfaces())
               {
                  log.debug("Proxy Interface for JNDI Name " + jndiName + ": " + in);
               }
            }
            Util.rebind(this.getContainer().getInitialContext(), jndiName, proxy);
         }

        
      } catch (NamingException e)
      {
         NamingException namingException = new NamingException("Could not bind session proxy with ejb name "
               + getContainer().getEjbName() + " into JNDI under jndiName: "
               + getContainer().getInitialContext().getNameInNamespace() + "/" + this.getJndiName());
         namingException.setRootCause(e);
         throw namingException;
      }
   }
  
  
   /**
    * Returns whether this Proxy Factory is local.  A Hack until EJB3 Proxy
    * is in place, but this keeps us moving forward easily.
    *
    * @deprecated Hack
    * @return
    */
   @Deprecated
   protected boolean isLocal()
   {
      return false;
   }

   /**
    * Whether or not to bind the home and business interfaces together
    *
    * @return
    */
   protected abstract boolean bindHomeAndBusinessTogether();
  
   protected Object constructProxyBusiness(InvocationHandler handler)
   {
      // Return
      return this.constructProxy(handler, SpecificationInterfaceType.EJB30_BUSINESS);
   }
  
   protected Object constructEjb21Proxy(InvocationHandler handler)
   {
      // Return
      return this.constructProxy(handler, SpecificationInterfaceType.EJB21);
   }
  
   /**
    * Construct a new Proxy of the specified type using the
    * specified handler as argument to the Constructor
    *
    * @param handler
    * @param specType
    * @return
    */
   protected Object constructProxy(final InvocationHandler handler, SpecificationInterfaceType specType)
   {
      // Initialize
      Object obj = null;

      try
      {
         // Business Proxy
         if (specType.equals(SpecificationInterfaceType.EJB30_BUSINESS))
         {
            obj = this.businessProxyConstructor.newInstance(handler);
         }
         // EJBObject/EJBLocalObject
         else if (specType.equals(SpecificationInterfaceType.EJB21))
         {
            // If there's no EJB21 View
            if (this.ejb21ProxyConstructor == null)
            {
               throw new IllegalStateException(
                     "EJB3 Specification Violation Section 4.3.3: \""
                           + "Only session beans with a remote EJBObject / local EJBLocalObject interface can call this method.");
            }

            obj = this.ejb21ProxyConstructor.newInstance(handler);
         }
      }
      catch (InstantiationException e)
      {
         throw new RuntimeException(e);
      }
      catch (IllegalAccessException e)
      {
         throw new RuntimeException(e);
      }
      catch (InvocationTargetException e)
      {
         Throwable t = e.getTargetException();
         if (t instanceof RuntimeException)
            throw (RuntimeException) t;
         throw new RuntimeException(t);
      }

      // Ensure Proxy object was created
      assert obj != null : "Proxy Object must not be null";

      // Return
      return obj;
   }

  
   protected void setContainer(SessionSpecContainer container)
   {
      this.container = container;
      this.containerGuid = Ejb3Registry.guid(container);
      this.containerClusterUid = Ejb3Registry.clusterUid(container);
      this.isClustered = container.isClustered();
   }
  
   protected SessionSpecContainer getContainer()
   {
      if (container == null)
      {
         container = (SessionSpecContainer)Ejb3Registry.findContainer(containerGuid);
        
         if (container == null && isClustered)
            container = (SessionSpecContainer)Ejb3Registry.getClusterContainer(containerClusterUid);
      }
     
      return container;
   }
  
   /**
    * Obtains interfaces to be used in the business proxy
    *
    * @return
    */
   protected Class<?>[] getInterfacesForBusinessProxy()
   {
      return this.getInterfacesForProxy(this.getProxyAccessType(), SpecificationInterfaceType.EJB30_BUSINESS);
   }
  
   /**
    * Obtains interfaces to be used in the EJB21 proxy.  Returns null if none defined
    *
    * @return
    */
   protected Class<?>[] getInterfacesForEjb21Proxy()
   {
      return this.getInterfacesForProxy(this.getProxyAccessType(), SpecificationInterfaceType.EJB21);
   }
  
   /**
    * Returns an array of interfaces to be used for the proxy;
    * will return null if none are defined.
    *
    * @param accessType
    * @param specType
    * @return
    */
   private Class<?>[] getInterfacesForProxy(ProxyAccessType accessType, SpecificationInterfaceType specType)
   {

      // Initialize
      Set<Class<?>> interfaces = new HashSet<Class<?>>();
      SessionContainer container = this.getContainer();

      // Initialize array of interfaces
      Set<Class<?>> intfs = new HashSet<Class<?>>();

      // If Local
      if (accessType.equals(ProxyAccessType.LOCAL))
      {

         // If business
         if (specType.equals(SpecificationInterfaceType.EJB30_BUSINESS))
         {
            intfs.addAll(Arrays.asList(ProxyFactoryHelper.getLocalBusinessInterfaces(container)))
           
            // If binding home with local business
            if(this.bindHomeAndBusinessTogether())
            {
               Class<?> home = this.getHomeType();
               if (home != null)
               {
                  intfs.add(home);
               }
            }
         }
         // If EJBLocalObject
         else
         {
            // Add local interfaces
            intfs.addAll(Arrays.asList(ProxyFactoryHelper.getLocalInterfaces(container)));
           
            // If no interfaces
            if (intfs.size() == 0)
            {
               return null;
            }
           
            // Add EJBLocalObject
            intfs.add(EJBLocalObject.class);
         }
      }
      // If remote
      else
      {
         // If business
         if (specType.equals(SpecificationInterfaceType.EJB30_BUSINESS))
         {
            intfs.addAll(Arrays.asList(ProxyFactoryHelper.getRemoteBusinessInterfaces(container)));  
           
            // If binding home with remote business
            if(this.bindHomeAndBusinessTogether())
            {
               Class<?> home = this.getHomeType();
               if (home != null)
               {
                  intfs.add(home);
               }
            }
           
         }
         // If EJBObject
         else
         {
            // Add remote interfaces
            intfs.addAll(Arrays.asList(ProxyFactoryHelper.getRemoteInterfaces(container)));
           
            // If no interfaces
            if (intfs.size() == 0)
            {
               return null;
            }
           
            // Add EJBObject
            intfs.add(EJBObject.class);
         }
      }

      // Add all interfaces
      for (Class<?> interfaze : intfs)
      {
         interfaces.add(interfaze);
      }

      // Add JBossProxy
      interfaces.add(JBossProxy.class);

      // Return
      return interfaces.toArray(new Class[]
      {});
   }
  
   /**
    * Defines the access type for this Proxies created by this Factory
    *
    * @return
    */
   protected abstract ProxyAccessType getProxyAccessType();
  
   protected void setEjb21Objects(BaseSessionRemoteProxyInvocationHandler proxy)
   {
      proxy.setHandle(this.createHandle());
      proxy.setHomeHandle(getHomeHandle());
      proxy.setEjbMetaData(getEjbMetaData());
   }
  
   abstract protected Handle createHandle();
  
   protected HomeHandle getHomeHandle()
   {
      EJBContainer ejbContainer = (EJBContainer)container;
     
      HomeHandleImpl homeHandle = null;
     
      RemoteBinding remoteBindingAnnotation = ejbContainer.getAnnotation(RemoteBinding.class);
      if (remoteBindingAnnotation != null)
         homeHandle = new HomeHandleImpl(ProxyFactoryHelper.getHomeJndiName(container));
     
      return homeHandle;
   }
  
   /**
    * Returns the interface type for Home
    *
    * @return
    */
   protected abstract Class<?> getHomeType();
  
   protected String getJndiName()
   {
      SessionSpecContainer container = this.getContainer();
      JBossSessionBeanMetaData md = container.getMetaData();
      String jndiName = md.determineResolvedJndiName(null);
      return jndiName;
   }

   protected String getHomeJndiName()
   {
      SessionSpecContainer container = this.getContainer();
      JBossSessionBeanMetaData md = container.getMetaData();
      String home = md.getHome();
      String jndiName = md.determineResolvedJndiName(home);
      return jndiName;
   }
  
   public final String getJndiNameProxyFactory()
   {
      return this.getJndiName() + BaseSessionProxyFactory.JNDI_NAME_SUFFIX_PROXY_FACTORY;
   }
  
   protected EJBMetaData getEjbMetaData()
   {
      Class<?> remote = null;
      Class<?> home = null;
      Class<?> pkClass = Object.class;
      HomeHandleImpl homeHandle = null;
     
      EJBContainer ejbContainer = (EJBContainer)container;
     
      Class<?>[] remotes = ProxyFactoryHelper.getRemoteInterfaces(this.getContainer());
      if (remotes != null && remotes.length > 0)
      {
         remote = remotes[0];
      }
      RemoteHome homeAnnotation = ejbContainer.getAnnotation(RemoteHome.class);
      if (homeAnnotation != null)
         home = homeAnnotation.value();
      RemoteBinding remoteBindingAnnotation = ejbContainer.getAnnotation(RemoteBinding.class);
      if (remoteBindingAnnotation != null)
         homeHandle = new HomeHandleImpl(remoteBindingAnnotation.jndiBinding());
     
      EJBMetaDataImpl metadata = new EJBMetaDataImpl(remote, home, pkClass, true, false, homeHandle);
     
      return metadata;
   }  
  
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
   {
      containerGuid = in.readUTF();
      containerClusterUid = in.readUTF();
      isClustered = in.readBoolean();
     
      if (getContainer() == null)
         throw new EJBException("Invalid (i.e. remote) invocation of local interface (null container) for " + containerGuid);
   }

   public void writeExternal(ObjectOutput out) throws IOException
   {
      out.writeUTF(containerGuid);
      out.writeUTF(containerClusterUid);
      out.writeBoolean(isClustered);
   }
  
   /**
    * Ensures that an EJB 2.1 view is complete; the following rules apply:
    *
    * 1) If EJBHome/EJBLocalHome is defined, at least one EJBObject/EJBLocalObject is defined. 
    * 2) If one EJBObject/EJBLocalObject is defined, an EJBHome/EJBLocalHome is defined.
    *
    * @param home
    * @param localOrRemoteInterfaces
    * @throws RuntimeException
    */
   protected void validateCompleteEjb21View(Class<?> home, Class<?>[] localOrRemoteInterfaces) throws RuntimeException
   {
      // Ensure specified home is EJBHome or EJBLocalHome
      assert (home == null || (EJBHome.class.isAssignableFrom(home) || EJBLocalHome.class.isAssignableFrom(home)));

      // Ensure all interfaces passed are either EJBObject or EJBLocalObject
      for (Class<?> localOrRemoteInterface : localOrRemoteInterfaces)
      {
         assert (EJBObject.class.isAssignableFrom(localOrRemoteInterface) || EJBLocalObject.class
               .isAssignableFrom(localOrRemoteInterface));
      }

      // If home is defined and there are no local/remote interfaces
      if (home != null && localOrRemoteInterfaces.length == 0)
      {
         throw new RuntimeException("EJBTHREE-1075: " + container.getBeanClassName() + " defines home"
               + " but provides no local/remote interfaces extending " + EJBLocalObject.class.getName() + "/"
               + EJBObject.class.getName() + "; EJB 2.1 view cannot be realized");
      }

      // If local/remote interfaces are defined, but no remote home
      if (home == null && localOrRemoteInterfaces.length != 0)
      {
         throw new RuntimeException("EJBTHREE-1075: " + container.getBeanClassName()
               + " defines local/remote interfaces" + " but provides no home; EJB 2.1 view cannot be realized");
      }
   }
  
   /**
    * Validates that the specified EJB2.1 Home interface returns only
    * valid remote/local interfaces from "create<METHOD>" methods.  If no
    * home is defined, the method will return without further checks
    *
    * @param home
    */
   protected void validateHomeReturnsNoBusinessInterfaces(Class<?> home)
   {
      // Only perform if home is defined; otherwise no EJB2.1 view
      if(home==null)
      {
         return;
      }
     
      // Sanity checks
      assert EJBHome.class.isAssignableFrom(home) || EJBLocalHome.class.isAssignableFrom(home) : "Specified home interface, "
            + home.getName() + ", must be of type " + EJBHome.class.getName() + " or " + EJBLocalHome.class.getName();
      assert home.isInterface() : "Specified home interface, " + home.getName() + " is not an interface.";

      // Initialize
      Set<Method> creates = new HashSet<Method>();

      // Obtain all "create<METHOD>" methods
      Method[] all = home.getDeclaredMethods();

      // For each method
      for (Method method : all)
      {
         // If a "create<METHOD>" method
         if (method.getName().startsWith(BaseSessionProxyFactory.METHOD_PREFIX_EJB21_CREATE))
         {
            // Add to the Set of Creates
            creates.add(method);
         }
      }

      // For all "create<METHOD>" methods
      for (Method create : creates)
      {
         // Init
         boolean isLocal = true;

         // Set as remote if applicable
         if (EJBHome.class.isAssignableFrom(home))
         {
            isLocal = false;
         }

         // If local (EJBLocalHome)
         if (isLocal)
         {
            // Validate return type is local interface
            if (!EJBLocalObject.class.isAssignableFrom(create.getReturnType()))
            {
               throw new RuntimeException("EJB 3 Core Specification Section 4.6.10: "
                     + "The return type for a create<METHOD> method must be"
                     + " the session bean's local interface type.  " + home.getName() + " has method "
                     + create.getName() + " which returns " + create.getReturnType().getName() + ". [EJBTHREE-1059]");
            }
         }
         // If remote (EJBHome)
         else
         {
            // Validate return type is remote interface
            if (!EJBObject.class.isAssignableFrom(create.getReturnType()))
            {
               throw new RuntimeException("EJB 3 Core Specification Section 4.6.8: "
                     + "The return type for a create<METHOD> method "
                     + "must be the session bean’s remote interface type.  " + home.getName() + " has method "
                     + create.getName() + " which returns " + create.getReturnType().getName() + ". [EJBTHREE-1059]");
            }
         }
      }
   }
  
   /**
    * Validates that any EJB2.1 Views associated with this ProxyFactory
    * are valid
    *
    * @param home
    * @param localOrRemoteInterfaces
    * @throws RuntimeException
    */
   protected void validateEjb21Views(Class<?> home,Class<?>[] localOrRemoteInterfaces) throws RuntimeException
   {
      // Ensure EJB2.1 Views are complete (EJBTHREE-1075)
      this.validateCompleteEjb21View(home, localOrRemoteInterfaces);
     
      // Ensure EJB2.1 Home returns only local/remote interfaces
      this.validateHomeReturnsNoBusinessInterfaces(home);
   }
    
   /**
    * Validates that any EJB2.1 Views associated with this ProxyFactory
    * are valid
    */
   protected abstract void validateEjb21Views();
}
TOP

Related Classes of org.jboss.ejb3.proxy.factory.BaseSessionProxyFactory

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.