14 Replies Latest reply on Nov 25, 2010 5:00 AM by st_patriick

    Externalizing Scope: Resolving EJB References and Endpoints

    alrubinger

      We currently leverage DeploymentUnit to address scoping while resolving EJB references or endpoints.  For instance:

       

      http://anonsvn.jboss.org/repos/jbossas/projects/weld-int/trunk/ejb/src/main/java/org/jboss/weld/integration/ejb/JBossSessionObjectReference.java

       

      This class is used by Weld to keep a hook into the EJB Container.  Because it's backed by the jboss-deployers (structure) SPI, we end up leaking out into unrelated concerns.  Ultimately Brian advises that we cannot replicate this class safely for use in clustering.

       

      From what I can tell, all we need from DeploymentUnit is a scoping mechanism used to resolve references.  My first reaction is to create a component which represents this information in a safely Serializable manner.

       

      From here we might reuse this scoping in a more generic injection framework (which I suspect we'll need soon anyway with the coming of E-EJB).

       

      S,

      ALR

        • 1. Re: Externalizing Scope: Resolving EJB References and Endpoints
          pmuir

          The motivation for this is to allow Weld to cluster with EJBs in JBoss AS - currently it won't work.

           

          CDI specifies the session and conversations contexts as being passivation capable, both of which are ultimately backed by the HTTP session. The appraoch Weld uses with Managed Beans is to put an object into the HTTP session which references both the Bean (the metadata) and the contextual instance. We take the same approach with EJBs - except that in this case the contextual instance contains not the "real" instance, but a SessionObjectReference which Weld uses to retrieve the underly EJB.

           

          I'm not fussed how we solve this problem, whether by changing the way Weld works, or adding something to EJB 3. I don't have a good idea how to change this in Weld tho ;-)

          • 2. Re: Externalizing Scope: Resolving EJB References and Endpoints
            alrubinger

            Other uses for easily-consumable injectors:

             

            • Arquillian; injects @EJBs
            • ejb3-mc-int, injects @EJBs into MC Beans
            • E-EJB3 or the ejb3-core test suite

             

            S,

            ALR

            • 3. Re: Externalizing Scope: Resolving EJB References and Endpoints
              wolfc
              Again, this problem was already resolved a long time ago. It's called home interfaces.
              • 4. Re: Externalizing Scope: Resolving EJB References and Endpoints
                alrubinger

                How do Home Interfaces give Weld a hook to the Container/Endpoint which is safely replicable in a cluster?

                 

                S,

                ALR

                • 5. Re: Externalizing Scope: Resolving EJB References and Endpoints
                  pmuir

                  BTW the reason we need to hold this reference beyond creation is to allow us to request the EJB is removed at a later date.

                   

                  CDI can do this because it actively manages the lifecycle of a bean - for example a  session scoped bean has a well defined point at which it is destroyed (when the http session ends) - and needs to request that the EJB container removes the EJB.

                   

                  As the session is a passivating scope, and can be replicated, we also need to hold this reference across passivation.

                  • 6. Re: Externalizing Scope: Resolving EJB References and Endpoints
                    alrubinger

                    I see what you're saying now.

                     

                    Weld needs to defer SFSB removal.  But they don't need a hook to the Container at all.  They can do:

                     

                    EJBHome home = context.lookup("MyEJB/home");
                    EJBObject object = home.create();
                    // Do stuff, then later:
                    home.remove(object.getHandle());
                    

                     

                    Then you don't need Endpoint at all..

                     

                    S,

                    ALR.

                    • 7. Re: Externalizing Scope: Resolving EJB References and Endpoints
                      wolfc

                      Exactly, were it not that EJB 3 beans don't have a home.

                      So we need to bind an orphanage for Weld to adopt EJB 3 beans.

                      • 8. Re: Externalizing Scope: Resolving EJB References and Endpoints
                        alrubinger

                        I've always wondered about this anyway.  Because injection of a SFSB makes little sense; it can timeout and become dead.  Better to inject an EJB3-compatible "Home"-type thing.  So a vendor-specific extension for us is what this sounds like.  Confirm we're on the same page now?

                         

                        The other points about resolving references for injection is another concern entirely; an external resolver w/ minimal dependencies.

                         

                        S,

                        ALR

                        • 9. Re: Externalizing Scope: Resolving EJB References and Endpoints
                          alrubinger

                          Some notes from our discussion today:

                           

                          This cannot work:

                           

                          //API:
                          /**
                           * All EJB3.x SFSBs will have this bound into JNDI
                           * at default: [appName]/moduleName/beanName/home
                           */
                          interface StatefulSessionHome
                          {
                              void remove(StatefulSessionHandle)
                          
                              boolean exists(StatefulSessionHandle);
                          
                              <T> T create(T type) throws IllegalArgumentException;
                          }
                          
                          //API:
                          /**
                           * All SFSB proxies may be cast to this type
                           * by the client
                           */
                          interface StatefulSessionHandle
                          {
                              // Marker only from the client view   
                          }
                          
                          //SPI:
                          /**
                           * This is how the container knows the ID of the session to remove;
                           * all SFSB proxies will implement this method
                           */
                          interface StatefulSessionHandleProvider extends StatefulSessionHandle
                          {
                               Object getId();
                          }
                          
                          
                          // Usage:
                          StatefulSessionHome home =  (StatefulSessionHome)context.lookup("beanName/home");
                          MyBusiness bean = home.create(MyBusiness.class);
                          home.remove((StatefulSessionHandle)bean);
                          
                          // Traditional JNDI lookup removal:
                          MyBusiness bean = context.lookup("beanName/remote");
                          home.remove((StatefulSessionHandle)bean);
                          

                           

                          Again, this is because we can't define *any* methods upon a proxy which is for business interfaces; we could introduce conflicts in method names.  In the case above, no user code could have "getId();" for example.

                           

                          An alternative way is to do something like have a Home per EJB Container.  Then the home knows how to go into the internals of the proxy handler to extract the ID.  Jaikiran wrote up a nice example:

                           

                          /***************** client (weld-int) *********************/
                          // user looks up or injects a SFSB (as usual)
                          UserSFSBInterface proxy = ...//get it as usual
                          
                          // now get a StatefulSessionHandleProvider (this needs discussion, but let's say it's available in JNDI for each SFSB)
                          StatefulSessionHandleProvider handleProvider = ctx.lookup("beanName/handleprovider");
                          StatefulSessionHandle handle = handleProvider.getHandle(proxy);
                          // do some stuff
                          handle.destroy();
                          
                          
                          
                          /***************** SPI*********************/
                          StatefulSessionHandleProvider
                          {
                            StatefulSessionHandle getHandle(Object proxy);
                          }
                          
                          StatefulSessionHandle
                          {
                            destroy();
                          
                          }
                          
                          /***************** SPI implementation (for nointerface view) *********************/
                          // Individual handle provider impl
                          JavassistProxyBasedHandleProvider implements StatefulSessionHandleProvider
                          {
                          
                           StatefulSessionHandle getHandle(Object proxy)
                           {
                            // do some checks on proxy 
                            // and return a handle
                            new JavassistBasedSFSBHandle(proxy)
                           }
                          }
                          
                          // JavassistBased handle for nointerface view
                          JavassistBasedSFSBHandle implements StatefulSessionHandle
                          {
                          
                            Serializable sessionId;
                          
                            JavassistBasedSFSBHandle(Object proxy)
                            {
                               // this knows the magic to get the session id from proxy
                              this.sessionId = doSomeMagicOnProxy();
                              
                            }
                          
                            destroy()
                            {
                              // destroy the session using hte session id
                                 container.destroy(this.sessionId);
                            }
                          
                          }
                          

                           

                          Too bad here we'd need a Home handle (HandleProvider in the example above) per container type.

                           

                          S,

                          ALR

                          • 10. Re: Externalizing Scope: Resolving EJB References and Endpoints
                            alrubinger

                            Doing today:

                             

                            • Creation of a "sessionmanager" component to define the SPI
                            • Digging into what ejb3-proxy needs to bind this
                            • Making a base impl for business local and business remote views
                            • Then we need another impl for nointerface

                             

                            S,

                            ALR

                            • 11. Re: Externalizing Scope: Resolving EJB References and Endpoints
                              alrubinger

                              https://jira.jboss.org/jira/browse/EJBTHREE-2011

                               

                              Client usage is shown in the ClientUsageUnitTest:

                               

                              /**
                               * Simple demonstration of client contracts {@link SessionManager} 
                               * to prove / document its use
                               *
                               * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
                               * @version $Revision: $
                               */
                              public class ClientUsageUnitTest
                              {
                              
                                 /**
                                  * A mock {@link SessionManagerFactory} which is represented to the compiler
                                  * as type {@link Object} because JNDI lookups will provide no type information
                                  */
                                 private static Object jndiEntry = new SessionManagerFactory()
                                 {
                              
                                    @Override
                                    public <T> SessionManager<T> get(Class<T> businessInterface) throws IllegalArgumentException
                                    {
                                       return new SessionManager<T>()
                                       {
                              
                                          @Override
                                          public T create(Class<T> beanInterface) throws IllegalArgumentException
                                          {
                                             // TODO Auto-generated method stub
                                             return null;
                                          }
                              
                                          @Override
                                          public boolean exists(T proxy) throws IllegalArgumentException
                                          {
                                             // TODO Auto-generated method stub
                                             return false;
                                          }
                              
                                          @Override
                                          public void remove(T proxy) throws NoSuchEJBException
                                          {
                              
                                          }
                                       };
                                    }
                                 };
                              
                                 /**
                                  * Shows the client usage of {@link SessionManagerFactory} and
                                  * {@link SessionManager}; doesn't really test anything (after all this
                                  * is an API component).
                                  */
                                 @Test
                                 public void showClientUsage()
                                 {
                              
                                    // This step mocks a JNDI lookup to a factory specific to a SFSB
                                    final SessionManagerFactory factory = (SessionManagerFactory) jndiEntry;
                              
                                    // Get a manager specific to a bean interface for the SFSB
                                    final SessionManager<String> manager = factory.get(String.class);
                              
                                    /*
                                     * Invoke its operations in a typesafe way
                                     */
                              
                                    // Create a new session
                                    final String created = manager.create(String.class);
                              
                                    // Session exists?
                                    manager.exists(created);
                              
                                    // Remove the session
                                    manager.remove(created);
                              
                                 }
                              }
                              

                               

                              Is this the view we'd like to proceed?

                               

                              S,

                              ALR

                              • 12. Re: Externalizing Scope: Resolving EJB References and Endpoints
                                marius.bogoevici

                                Andrew,

                                 

                                I think that the API that you're suggesting would work well with JBossSessionObjectReference - so the next question is how quickly can you make it available to be consumed by weld-int?

                                 

                                Thanks,

                                Marius

                                • 13. Re: Externalizing Scope: Resolving EJB References and Endpoints
                                  alrubinger

                                  Looks like it's time to reschedule this in, then.

                                   

                                  The bigger issues are how we integrate it in with other dependent modules like jboss-metadata-ejb, etc.  I'll dig back in starting tomorrow AM and see what estimates I can provide.


                                  S,

                                  ALR

                                  • 14. Re: Externalizing Scope: Resolving EJB References and Endpoints
                                    st_patriick

                                    Is this the reason why SFSBs inside a CDI-enabled module are not clusterable?

                                     

                                    Currently, I get an exception during deserialization when such SFSB is replicated to another cluster node. I was thinking of creating a separate bug for this, but it seems that it would be a duplicate of https://jira.jboss.org/browse/JBAS-8293 that points to this forum thread.