9 Replies Latest reply on May 22, 2009 1:45 AM by jaikiran

    Mandating the presence of local/remote interface in SessionC

    jaikiran

      While testing the no-interface support, i found that the SessionContainer mandates the presence of either a local or a remote interface for beans:

      Caused by: java.lang.RuntimeException: Bean Class org.jboss.ejb3.nointerface.test.viewcreator.SimpleSLSBWithoutInterface has no local, webservice, or remote interfaces defined and does not implement at least one business interface: SimpleSLSBWithoutInterface
       at org.jboss.ejb3.proxy.factory.ProxyFactoryHelper.getLocalAndBusinessLocalInterfaces(ProxyFactoryHelper.java:255)
       at org.jboss.ejb3.proxy.factory.ProxyFactoryHelper.getLocalBusinessInterfaces(ProxyFactoryHelper.java:550)
       at org.jboss.ejb3.session.SessionContainer.resolveBusinessInterfaces(SessionContainer.java:192)
       at org.jboss.ejb3.EJBContainer.instantiated(EJBContainer.java:1564)
       at org.jboss.ejb3.session.SessionContainer.instantiated(SessionContainer.java:182)
      ...
      


      The code in question is this:

      protected List<Class<?>> resolveBusinessInterfaces()
       {
       // Obtain all business interfaces
       List<Class<?>> list = new ArrayList<Class<?>>();
       list.addAll(Arrays.asList(ProxyFactoryHelper.getLocalBusinessInterfaces(this)));
       list.addAll(Arrays.asList(ProxyFactoryHelper.getRemoteBusinessInterfaces(this)));
      
       return list;
       }
      
      


      public static Class<?>[] getLocalAndBusinessLocalInterfaces(Container container)
       {
       // Initialize
       Set<Class<?>> localAndBusinessLocalInterfaces = new HashSet<Class<?>>();
      
       // Obtain Bean Class
       Class<?> beanClass = container.getBeanClass();
      
       // Obtain @Local
       Local localAnnotation = ((EJBContainer) container).getAnnotation(Local.class);
      
       // Obtain @LocalHome
       LocalHome localHomeAnnotation = ((EJBContainer) container).getAnnotation(LocalHome.class);
      
       // Obtain @Remote
       Remote remoteAnnotation = ((EJBContainer) container).getAnnotation(Remote.class);
      
       // Obtain Remote and Business Remote interfaces
       Class<?>[] remoteAndBusinessRemoteInterfaces = ProxyFactoryHelper.getRemoteAndBusinessRemoteInterfaces(container);
      
       // Obtain all business interfaces from the bean class
       Set<Class<?>> businessInterfacesImplementedByBeanClass = ProxyFactoryHelper.getBusinessInterfaces(beanClass);
      
       // Obtain all business interfaces directly implemented by the bean class (not including supers)
       Set<Class<?>> businessInterfacesDirectlyImplementedByBeanClass = ProxyFactoryHelper.getBusinessInterfaces(
       beanClass, false);
      
       // Determine whether Stateful or Stateless
       boolean isStateless = (container instanceof StatelessContainer) ? true : false;
      
       
      ... // lot more stuff which is trimmed from the post
      
       // If no local interfaces have been defined/discovered
       else
       {
       // Obtain WS Endpoint
       String endpoint = ProxyFactoryHelper.getEndpointInterface(container);
      
       // If neither WS Endpoint or remotes are defined
       if (remoteAndBusinessRemoteInterfaces.length == 0 && endpoint == null)
       throw new RuntimeException(
       "Bean Class "
       + beanClass.getName()
       + " has no local, webservice, or remote interfaces defined and does not implement at least one business interface: "
       + container.getEjbName());
      
       }
      
       // No local or business local interfaces discovered
       return new Class<?>[]
       {};
       }
      


      Questions:

      1) From what i remember, this sort of logic is already in place in some deployer. So do we need this here again?
      2) Shouldn't this logic be based on metadata which already has all the necessary information? Instead of looking for the annotations and other stuff on the bean class (again)
      3) The restriction will not hold good for no-interface view for EJB 3.1


        • 1. Re: Mandating the presence of local/remote interface in Sess
          jaikiran

          I was planning to reimplement the resolveBusinessInterfaces method in SessionContainer to (use metadata):

          Index: src/main/java/org/jboss/ejb3/session/SessionContainer.java
          ===================================================================
          --- src/main/java/org/jboss/ejb3/session/SessionContainer.java (revision 89162)
          +++ src/main/java/org/jboss/ejb3/session/SessionContainer.java (working copy)
          @@ -26,7 +26,6 @@
           import java.rmi.NoSuchObjectException;
           import java.rmi.Remote;
           import java.util.ArrayList;
          -import java.util.Arrays;
           import java.util.Hashtable;
           import java.util.List;
           import java.util.Map;
          @@ -62,7 +61,6 @@
           import org.jboss.ejb3.endpoint.SessionFactory;
           import org.jboss.ejb3.proxy.clustered.objectstore.ClusteredObjectStoreBindings;
           import org.jboss.ejb3.proxy.clustered.registry.ProxyClusteringRegistry;
          -import org.jboss.ejb3.proxy.factory.ProxyFactoryHelper;
           import org.jboss.ejb3.proxy.impl.factory.session.SessionProxyFactory;
           import org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase;
           import org.jboss.ejb3.proxy.spi.container.InvokableContext;
          @@ -71,6 +69,8 @@
           import org.jboss.ha.framework.server.HATarget;
           import org.jboss.logging.Logger;
           import org.jboss.metadata.ejb.jboss.JBossSessionBeanMetaData;
          +import org.jboss.metadata.ejb.spec.BusinessLocalsMetaData;
          +import org.jboss.metadata.ejb.spec.BusinessRemotesMetaData;
           import org.jboss.serial.io.MarshalledObjectForLocalCalls;
          
           /**
          @@ -187,12 +187,43 @@
           @Override
           protected List<Class<?>> resolveBusinessInterfaces()
           {
          - // Obtain all business interfaces
          - List<Class<?>> list = new ArrayList<Class<?>>();
          - list.addAll(Arrays.asList(ProxyFactoryHelper.getLocalBusinessInterfaces(this)));
          - list.addAll(Arrays.asList(ProxyFactoryHelper.getRemoteBusinessInterfaces(this)));
          -
          - return list;
          + // first find business locals
          + BusinessLocalsMetaData businessLocals = this.getMetaData().getBusinessLocals();
          + List<Class<?>> businessInterfaces = new ArrayList<Class<?>>();
          + for (String businessLocal : businessLocals)
          + {
          + try
          + {
          + Class<?> localBusinessInterface = this.classloader.loadClass(businessLocal);
          + businessInterfaces.add(localBusinessInterface);
          +
          + }
          + catch (ClassNotFoundException cnfe)
          + {
          + throw new RuntimeException("Business local interface " + businessLocal + " for bean " + this.getEjbName()
          + + " , in deployment " + this.getDeploymentUnit() + " , could not be found", cnfe);
          + }
          +
          + }
          + // now business remotes
          + BusinessRemotesMetaData businessRemotes = this.getMetaData().getBusinessRemotes();
          + for (String businessRemote : businessRemotes)
          + {
          + try
          + {
          + Class<?> remoteBusinessInterface = this.classloader.loadClass(businessRemote);
          + businessInterfaces.add(remoteBusinessInterface);
          +
          + }
          + catch (ClassNotFoundException cnfe)
          + {
          + throw new RuntimeException("Business remote interface " + businessRemote + " for bean " + this.getEjbName()
          + + " , in deployment " + this.getDeploymentUnit() + " , could not be found", cnfe);
          + }
          +
          + }
          + // return the business interfaces (local and remote)
          + return businessInterfaces;
           }
          


          But looking at the earlier implementation, it used to take care of infering the EJB2.x style local interface from the @LocalHome (and similar for remote interface from @RemoteHome). I am not sure the metadata approach can handle that.

          • 2. Re: Mandating the presence of local/remote interface in Sess
            alrubinger

            This is some older check that I'd put in place. I don't think we yet have its equivalent in jboss-metadata, but it should be here:

            http://anonsvn.jboss.org/repos/jbossas/projects/metadata/trunk/src/main/java/org/jboss/metadata/validation/validator/ejb/jboss/

            And then you'd want to add it to the List of default validators here:

            http://anonsvn.jboss.org/repos/jbossas/projects/metadata/trunk/src/main/java/org/jboss/metadata/validation/chain/ejb/jboss/JBossMetaDataValidatorChain.java

            ..@ "getDefaultValidators()".

            S,
            ALR

            • 3. Re: Mandating the presence of local/remote interface in Sess
              alrubinger

              To add some more context, putting this Validator in the Chain gets picked up and run by:

              http://anonsvn.jboss.org/repos/jbossas/projects/ejb3/trunk/deployers/src/main/java/org/jboss/ejb3/deployers/Ejb3MetadataProcessingDeployer.java

              The benefits are:

              1) We can give the user a series of error messages (this thing is wrong, that thing is wrong) all at once so they can fix their deployment
              2) Validation is a concern that gets removed from ejb3-core and is testable outside using only a metadata object.

              S,
              ALR

              • 4. Re: Mandating the presence of local/remote interface in Sess
                jaikiran

                 

                "ALRubinger" wrote:
                This is some older check that I'd put in place. I don't think we yet have its equivalent in jboss-metadata, but it should be here:

                http://anonsvn.jboss.org/repos/jbossas/projects/metadata/trunk/src/main/java/org/jboss/metadata/validation/validator/ejb/jboss/


                As a first step, i will move this validation to the JBMETA validators. As a second step, when we have support for no-interface in metadata, we will have to reimplement this to throw an error only if the bean does not expose a no-interface view.




                • 5. Re: Mandating the presence of local/remote interface in Sess
                  jaikiran
                  • 6. Re: Mandating the presence of local/remote interface in Sess
                    jaikiran

                     

                    2) Validation is a concern that gets removed from ejb3-core and is testable outside using only a metadata object.


                    Do we have a validator API which can be used for validations which require the deployment unit or the classloader associated with the unit? The current validator interface works on just the metadata :

                    public interface Validator
                    {
                    
                     /**
                     * Validates the specified metadata against the configured
                     * validators
                     */
                     void validate(JBossMetaData md) throws ValidationException;
                    
                    }


                    For some validations, we need to have access to the classloader associated with the unit. Ex: To validate that a SLSB's home interface has exactly one create method (EJB 3.0 Specification 4.6.8 Bullet 4, 4.6.10 Bullet 4). Right now, such validations are being done in the core.


                    • 7. Re: Mandating the presence of local/remote interface in Sess
                      wolfc

                      Validation should happen within the proper context. In this the context class loader should be valid.

                      • 8. Re: Mandating the presence of local/remote interface in Sess
                        alrubinger

                         

                        "wolfc" wrote:
                        Validation should happen within the proper context. In this the context class loader should be valid.


                        Disagree. I think validation of a deployment should be able to happen from anywhere. Why make impositions we don't have to?

                        For instance, the "processors" do things like setting appropriate defaults upon metadata after everything's been merged, and this one gets the proper CL via the ctor:

                        http://anonsvn.jboss.org/repos/jbossas/projects/metadata/trunk/src/main/java/org/jboss/metadata/process/processor/ejb/jboss/SetDefaultLocalBusinessInterfaceProcessor.java

                        S,
                        ALR

                        • 9. Re: Mandating the presence of local/remote interface in Sess
                          jaikiran

                           

                          "wolfc" wrote:
                          Validation should happen within the proper context. In this the context class loader should be valid.


                          I don't think that's going to work. The validators come into picture when the deployment unit is being processed by the deployers. During this phase the TCCL can/will be different from the classloader associated with the deployment unit.