1 2 Previous Next 19 Replies Latest reply on Oct 13, 2010 12:16 PM by marius.bogoevici

    Deferring instance creation/injection to CDI

    marius.bogoevici

      There are a number of JIRA items which currently require CDI to be able to intervene in the process of EJB and EJB interceptor instance creation process (these may not be all the items, but :

       

      https://jira.jboss.org/jira/browse/WELDINT-27 (apply decorators on EJB instances)

      https://jira.jboss.org/jira/browse/WELDINT-9 (perform injection on EJBs directly and not through an interceptor)

      https://jira.jboss.org/jira/browse/JBAS-7046 (perform injection on EJB interceptors)
      https://jira.jboss.org/jira/browse/WELDINT-31 (use @Inject constructors for session EJBs)

       

      So the main question at this point is whether there is currently a way to hook Weld into the EJB instantiation process? And if not, then we should probably start discussing this.

       

      Thanks,

      Marius

        • 1. Re: Deferring instance creation/injection to CDI
          alrubinger

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

           

          Looks like we can extract the instantiation logic, and let EJB3 provide it's own implementation.  CDI, if it likes, can swap out for their own during deployment.  I've committed r102436 to see if this can be a workable solution for both teams.

           

          /**
           * Contract of a component responsible for creation of EJB bean 
           * and instances.  Though the EJB specification dictates 
           * the use of a no-arg constructor, contextual EJBs defined by JSR-299 
           * provides for additional features such as injected constructor parameters, 
           * so alternate implementations of this type may be used by the 
           * EJB container.
           *
           * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
           * @version $Revision: $
           */
          public interface BeanInstantiator
          {
             //-------------------------------------------------------------------------------------||
             // Contracts --------------------------------------------------------------------------||
             //-------------------------------------------------------------------------------------||
          
             /**
              * Creates a new instance of the specified implementation {@link Class}
              * using the supplied parameters.  While the implementation class 
              * must be specified, <code>null</code> is a valid value for the parameters,
              * and will be treated as a 0-length array.
              * 
              * @throws IllegalArgumentException If the implementation class was not specified
              * or the implementation does not support parameters (while they've been supplied).
              * @throws InvalidConstructionParamsException If the implementation of this type does 
              * not support the parameters supplied.
              * @throws BeanInstantiationException If an unexpected error occurred in constructing the bean
              * instance
              */
             <T> T create(Class<T> implClass, Object[] parameters) throws IllegalArgumentException,
                   InvalidConstructionParamsException, BeanInstantiationException;
          }
          

           

          The EJB3 impl is simple:

           

          /**
           * Implementation of {@link BeanInstantiator} strictly conforming to the
           * EJB 3.1 Specification.  Any non-0-length or non-null parameter arguments to 
           * {@link Ejb31SpecBeanInstantiator#create(Class, Object[])}
           * will result in {@link InvalidConstructionParamsException}
           *
           * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
           * @version $Revision: $
           */
          public class Ejb31SpecBeanInstantiator implements BeanInstantiator
          {
          
             //-------------------------------------------------------------------------------------||
             // Class Members ----------------------------------------------------------------------||
             //-------------------------------------------------------------------------------------||
          
             /**
              * Logger
              */
             private static final Logger log = Logger.getLogger(Ejb31SpecBeanInstantiator.class.getName());
          
             /**
              * Message used in reporting exceptions during instantiation
              */
             private static final String MSG_PREFIX_INSTANTIATION_EXCEPTION = "Could not create new instance with no arguments of: ";
          
             //-------------------------------------------------------------------------------------||
             // Required Implementations -----------------------------------------------------------||
             //-------------------------------------------------------------------------------------||
          
             /**
              * {@inheritDoc}
              * @see org.jboss.ejb3.instantiator.spi.BeanInstantiator#create(java.lang.Class, java.lang.Object[])
              */
             public <T> T create(final Class<T> implClass, final Object[] parameters) throws IllegalArgumentException,
                   InvalidConstructionParamsException, BeanInstantiationException
             {
                // Precondition checks
                if (parameters != null && parameters.length > 0)
                {
                   throw InvalidConstructionParamsException
                         .newInstance("EJB Specification requires a no-argument constructor be invoked for bean instances on "
                               + implClass.getName());
                }
          
                // Instantiate
                final T obj;
                try
                {
                   obj = implClass.newInstance();
                }
                catch (final InstantiationException e)
                {
                   throw BeanInstantiationException.newInstance(MSG_PREFIX_INSTANTIATION_EXCEPTION + implClass, e);
                }
                catch (final IllegalAccessException e)
                {
                   throw BeanInstantiationException.newInstance(MSG_PREFIX_INSTANTIATION_EXCEPTION + implClass, e);
                }
          
                // Log
                if (log.isLoggable(Level.FINER))
                {
                   log.finer("Created: " + obj + "; instance of " + implClass.getName());
                }
          
                // Return
                return obj;
             }
          
          }
          

           

          S,

          ALR

          • 2. Re: Deferring instance creation/injection to CDI
            marius.bogoevici

            Andrew,

             

            Thanks for the update. I will look into this first thing tomorrow morning.

             

            Marius

            • 3. Re: Deferring instance creation/injection to CDI
              jaikiran

              Actually, as part of the container SPI component, the bean instantiation part has already been extracted out http://anonsvn.jboss.org/repos/jbossas/projects/ejb3/components/container/trunk/spi/src/main/java/org/jboss/ejb3/container/spi/EJBInstanceManager.java

               

              I guess, we should catch up over IRC to discuss about how these 2 relate.

              • 4. Re: Deferring instance creation/injection to CDI
                wolfc

                Container-spi is higher level.

                 

                This is something for behind EJBContainer.construct(). Note that the EJB container never has any arguments to pass, it's CDI which has to come up with the injectees.

                 

                What's the relation between BeanInstantiator and EJB container? Right now it looks like we can have a global instantiator given the implClass argument on create.

                 

                How to we threat interceptor instances? I don't think they are managed beans, but they need to be (CDI) injected.

                • 5. Re: Deferring instance creation/injection to CDI
                  marius.bogoevici

                  Adding to Carlo's comment: I don't think that there's an overlap either - what Andrew has proposed is essentially targeted towards allowing CDI to produce an instance which is:

                   

                  - constructed according to the CDI constructor (either the unique @Inject constructor, or the default one). I wonder if we do need to pass a list of arguments at all, since it's up to CDI to decide on the constructor to invoke and its parameters. Of course, having the ability to pass arguments may be a benefit in other cases.

                  - injected according to CDI rules concerning the processing of @Inject

                  - decorated according to the CDI

                   

                  I suppose that this leaves it to the container to:

                  - inject Java EE resources and fields (@Resource,@EJB references, etc)

                  - apply interceptors (for which we should stick with the current approach)

                   

                  So, the approach of "let EJB3 do the EJB3 work, let CDI do the CDI work" should work fine.

                   

                  Now, I guess we could do the latter directly from within CDI but it would complicate the lives of both CDI and EJB3.

                   

                  The other thing that Carlo mentioned is interceptors - so here there would be two possibilities, as discussed with Andrew on IRC:

                   

                  - leave instance creation + injection to CDI

                  - provide a callback through the SPI (postInterceptorCreation) so that CDI can do the injection.

                  • 6. Re: Deferring instance creation/injection to CDI
                    alrubinger

                    Jaikiran:

                     

                    Looks more like the Container SPI "EJBInstanceManager" more closely pertains to the creation of sessions, not the mechanics of creating bean instances.  Perhaps we rename?

                     

                    S,

                    ALR

                    • 7. Re: Deferring instance creation/injection to CDI
                      alrubinger

                      I've attached ejb3-core integration to the issue.

                       

                      S,

                      ALR

                      • 8. Re: Deferring instance creation/injection to CDI
                        marius.bogoevici

                        Restarting the discussion. In summary, what Weld/CDI needs is:

                         

                        1) EJB defers instance creation to an external component

                            - Weld deployer sets the instantiator components that will:

                                     a) invoke the applicable CDI constructor of the EJB class

                                     b) apply decorators on the EJB

                                     c) *not* apply interceptors on the EJB - for now, this remains a responsibility of the EJB container

                         

                        2) EJB container provides a hook for preprocessing interceptors after instantiation. The main reason is that EJB interceptors may be CDI-injected

                         

                        (note: could we reuse the same mechanism as for EJB instantiation, since it boils down to producing an instance of the class? )

                        • 9. Re: Deferring instance creation/injection to CDI
                          alrubinger

                          Marius Bogoevici wrote:

                           

                          Restarting the discussion. In summary, what Weld/CDI needs is:

                           

                          1) EJB defers instance creation to an external component

                              - Weld deployer sets the instantiator components that will:

                                       a) invoke the applicable CDI constructor of the EJB class

                                       b) apply decorators on the EJB

                                       c) *not* apply interceptors on the EJB - for now, this remains a responsibility of the EJB container

                          I believe I've now released something which accounts for this.

                           

                          http://github.com/jbossejb3/jboss-ejb3-bean-instantiator

                           

                          The above has been released as 1.0.0-alpha-1 and integrated into ejb3-core (ie. we now use the abstraction for EJB bean instance creation).  This has not yet been integrated into AS yet (as that relies upon a release of core, which is a formal release chain).

                           

                          The general idea is that you create an implementation of the SPI, and create an MC bean called ""org.jboss.ejb3.bean.BeanInstantiator".  Once you do this, the deployer impl will pick it up and wire things along such that the EJBContainer uses it.  So make one artifact which implements jboss-ejb3-instantiator-spi, and inside it put the impl class and a META-INF/weld-bean-instantiator-jboss-beans.xml which may look like:

                           

                           

                          <?xml version="1.0" encoding="UTF-8"?>
                           
                            <!--
                              Declares a EJB3 Bean Instantiator
                          -->
                          <deployment xmlns="urn:jboss:bean-deployer:2.0">
                           
                            <bean name="org.jboss.ejb3.bean.BeanInstantiator"
                              class="org.jboss.ejb3.instantiator.impl.Ejb31SpecBeanInstantiator" />
                           
                          </deployment>
                          

                           

                          Check out the layout of the jboss-ejb3-instantiator-impl module for our current legacy impl.  Basically you'll be writing the same thing.

                           

                          Also note that we currently don't support any construction parameters.  ejb3-core doesn't have any available to pass along.  So even though the contract accepts construction params, I'm passing in Object[]{}.  If that needs to be addressed...let me know and we'll figure something.

                           

                          Marius Bogoevici wrote:

                           

                          2) EJB container provides a hook for preprocessing interceptors after instantiation. The main reason is that EJB interceptors may be CDI-injected

                          (note: could we reuse the same mechanism as for EJB instantiation, since it boils down to producing an instance of the class? )

                           

                          I'm pretty sure we could do this too.  We have this bit of code, though I haven't verified if it's currently used or cruft:

                           

                           

                          public Object createInterceptor(Class<?> interceptorClass) throws InstantiationException, IllegalAccessException
                             {
                                Object instance = interceptorClass.newInstance();
                                InterceptorInjector interceptorInjector = interceptorInjectors.get(interceptorClass);
                                assert interceptorInjector != null : "interceptorInjector not found for " + interceptorClass;
                                interceptorInjector.inject(null, instance);
                                return instance;
                             }
                          

                           

                           

                          Plenty of room in there to both abstract away the instantiation or give some post-instantiation callback before the instance is returned.


                          S,

                          ALR

                          • 10. Re: Deferring instance creation/injection to CDI
                            marius.bogoevici

                            Great! I'll give instantiation a try today!

                             

                            From a Weld perspective, sending Object[] as constructor parameters works pretty well, since the decision of picking up a constructor and populating the parameters with actual values should rest with the CDI container.

                            • 11. Re: Deferring instance creation/injection to CDI
                              marius.bogoevici

                              I integrated this in http://svn.jboss.org/repos/jbossas/branches/weld-ejb3-int/

                               

                              (refers to a SNAPSHOT version of ejb3-depchain which contains references to the latest version of ejb3-core).

                               

                              For now, just the EJB31 instantiation support, I'll continue with Weld integration from there.

                              • 12. Re: Deferring instance creation/injection to CDI
                                jaikiran

                                There's a bit of integration that's remaining to be done in singleton container and the AS trunk. The singleton part is done in my local setup with a few minor changes to ejb3-core. I'll have a chat with ALR tomorrow and push these changes in.

                                • 13. Re: Deferring instance creation/injection to CDI
                                  marius.bogoevici

                                  Could we possibly change the way in which the instantiator instance is handed over to the Ejb3Deployer? That is, moving from creating the instantiator in the appropriate deployer and adding it as an attachment, to registering a BeanMetaData item and letting the MC do the wiring. The latter could be helpful, since for example in the case of Weld integration we need a reference to the BootstrapBean and letting the MC do the wiring would ensure both the dependency injection and proper ordering of components (e.g. EJBs including singletons are not started until the instantiator is ready).

                                   

                                  If the instantiator mechanism is to become more generic and used in other cases, then using the MC would help.

                                  • 14. Re: Deferring instance creation/injection to CDI
                                    alrubinger

                                    Marius Bogoevici wrote:

                                     

                                    Could we possibly change the way in which the instantiator instance is handed over to the Ejb3Deployer? That is, moving from creating the instantiator in the appropriate deployer and adding it as an attachment, to registering a BeanMetaData item and letting the MC do the wiring. The latter could be helpful, since for example in the case of Weld integration we need a reference to the BootstrapBean and letting the MC do the wiring would ensure both the dependency injection and proper ordering of components (e.g. EJBs including singletons are not started until the instantiator is ready).

                                     

                                    If the instantiator mechanism is to become more generic and used in other cases, then using the MC would help.

                                    We don't currently create the instantiator impl in the deployer.

                                     

                                    http://github.com/jbossejb3/jboss-ejb3-bean-instantiator/blob/master/deployer/src/main/java/org/jboss/ejb3/instantiator/deployer/BeanInstantiatorDeployer.java

                                     

                                     

                                    <!-- The BeanInstantiatorDeployer attaches a BeanInstantiator implementation to the EJB3 DU -->
                                      <bean name="org.jboss.ejb3.BeanInstantiatorDeployer"
                                        class="org.jboss.ejb3.instantiator.deployer.BeanInstantiatorDeployer">
                                        <constructor>
                                          <parameter name="beanInstantiator"><inject bean="org.jboss.ejb3.BeanInstantiator"/></parameter>
                                        </constructor>
                                      </bean>
                                    

                                     

                                    This picks up whatever MC bean named "org.jboss.ejb3.BeanInstantiator" and attaches is to the DU.

                                     

                                    Why not have your impl @Inject "BootstrapBean" and just install it under the same name?


                                    S,

                                    ALR

                                    1 2 Previous Next