1 2 Previous Next 22 Replies Latest reply: Nov 12, 2009 9:50 AM by Ales Justin RSS

AS weld-int

Kabir Khan Master

I'm looking at the as weld-int project how to integrate the work I have done on MC's weld-int.

Here's an outline of what I believe I have to do

== 1 - Initialize Weld =====
This is done by BootstrapBean.initialize(). The BeanMetaData for BootstrapBean is created by WeldBootstrapDeployer, and deployed by BeanMetaDataDeployer.


== 2 - Deploy the MC beans using WeldKernelControllerContext so that we can have Weld->MC injection, and also be pushed to weld for MC->Weld injection =====
a) I need the BeanManager. BootstrapBean is initialised, so I can call BB.getWeldManager(). I do however need access to the bean manager to use, which I can get from the (Flat)Deployment attachment. I do see some issues down the line if a deployment has several bean managers, but want to get something up and running before digging into that.
b) How do I make sure that WeldKernelControllerContexts are deployed instead of KernelControllerContexts? Replace BeanMetaDataDeployer with something else?

== 3 - Deploy Weld beans =====
This is done by BootstrapBean.boot() which is called as part of BootstrapBean's start phase. I need to make sure this happens after 2), which looks like it is the case in BootDeployerTestCase, due to some mysterious dependency on EjbContainer#1 (line 79)


  • 1. Re: AS weld-int
    Kabir Khan Master

     

    "kabir.khan@jboss.com" wrote:

    b) How do I make sure that WeldKernelControllerContexts are deployed instead of KernelControllerContexts? Replace BeanMetaDataDeployer with something else?

    Just noting down an idea before I finish for the weekend

    Maybe I need similar deployers to KernelDeploymentDeployer/BeanMetaDataDeployer that come before those. A WeldKernelDeploymentDeployer could kick in if the MC beans are part of a Weld deployment, it could remove the KernelDeployment attachment, then wrap it as a WeldKernelDeployment attachment, with a DeploymentVisitor to create WeldBeanMetaDatas which would be picked up by a WeldBeanMetaDataDeployer which would deploy WeldKernelControllerContexts. That would cover the MC beans that are part of a Weld deployment, the question is what about the MC beans that are not part of a weld deployment? Only the WeldKernelControllerContexts are currently registered for pushing to Weld for MC->Weld injection.



  • 2. Re: AS weld-int
    Ales Justin Master

     

    "kabir.khan@jboss.com" wrote:

    a) I need the BeanManager. BootstrapBean is initialised, so I can call BB.getWeldManager(). I do however need access to the bean manager to use, which I can get from the (Flat)Deployment attachment. I do see some issues down the line if a deployment has several bean managers, but want to get something up and running before digging into that.

    This is the name under which BootstrapBean is registered in MC:
    * String bootstrapName = DeployersUtils.getBootstrapBeanName(unit);

    "kabir.khan@jboss.com" wrote:

    b) How do I make sure that WeldKernelControllerContexts are deployed instead of KernelControllerContexts? Replace BeanMetaDataDeployer with something else?

    Yes.
    I would rather change BMDDeployer than go down that ugly path you describe in your 2nd post. :-)

    e.g.
    Have a list of ControllerContext creators in BMDDeployer,
    short circuit-ing on 1st return context instance; by default creating KCC.

    "kabir.khan@jboss.com" wrote:

    This is done by BootstrapBean.boot() which is called as part of BootstrapBean's start phase. I need to make sure this happens after 2), which looks like it is the case in BootDeployerTestCase, due to some mysterious dependency on EjbContainer#1 (line 79)

    This is the magic that does it:
     // call dynamic dependency creator for EJBs
     ParameterMetaDataBuilder install = bootstrap.addInstallWithParameters("createDepenencies", "DynamicDependencyCreator", null, ControllerState.CONFIGURED);
     install.addParameterMetaData(Object.class.getName(), bootstrapName);
     install.addParameterMetaData(Iterable.class.getName(), bootstrap.createInject(ejbServicesValue.getUnderlyingValue(), "ejbContainerNames"));
     install.addParameterMetaData(String.class.getName(), "Start");
     install.addParameterMetaData(String.class.getName(), "Start");
    
     /**
     * Create dependencies to target bean.
     *
     * @param targetName the target bean name
     * @param dependencies dependencies names
     * @param whenRequiredState when required state
     * @param dependentState dependencies dependent state
     */
     public void createDepenencies(Object targetName, Iterable<String> dependencies, String whenRequiredState, String dependentState)
     {
     if (targetName == null)
     throw new IllegalArgumentException("Null target name");
     if (dependencies == null)
     throw new IllegalArgumentException("Null dependecies");
    
     ControllerContext targetControllerContext = controller.getContext(targetName, null);
     if (targetControllerContext == null)
     throw new IllegalArgumentException("No such target bean installed: " + targetName);
    
     Throwable error = targetControllerContext.getError();
     if (error != null)
     throw new IllegalArgumentException("Target bean " + targetName + " is in Error state: " + error);
    
     ControllerState whenRequired;
     if (whenRequiredState == null)
     whenRequired = ControllerState.INSTALLED;
     else
     whenRequired = new ControllerState(whenRequiredState);
    
     ControllerState currentTargetState = targetControllerContext.getState();
     if (controller.getStates().isBeforeState(currentTargetState, whenRequired) == false)
     throw new IllegalArgumentException("Target bean " + targetName + " is already past " + whenRequiredState + " state: " + targetControllerContext);
    
     ControllerState dependent = null;
     if (dependentState != null)
     dependent = new ControllerState(dependentState);
    
     DependencyInfo di = targetControllerContext.getDependencyInfo();
     for (Object dependency : dependencies)
     {
     DependencyItem item = new AbstractDependencyItem(targetName, dependency, whenRequired, dependent);
     di.addIDependOn(item);
     }
     }
    

    ;-)


  • 3. Re: AS weld-int
    Kabir Khan Master

     

    "alesj" wrote:

    "kabir.khan@jboss.com" wrote:

    b) How do I make sure that WeldKernelControllerContexts are deployed instead of KernelControllerContexts? Replace BeanMetaDataDeployer with something else?

    Yes.
    I would rather change BMDDeployer than go down that ugly path you describe in your 2nd post. :-)

    e.g.
    Have a list of ControllerContext creators in BMDDeployer,
    short circuit-ing on 1st return context instance; by default creating KCC.


    I have done this for https://jira.jboss.org/jira/browse/JBDEPLOY-219

  • 4. Re: AS weld-int
    Ales Justin Master

     

    "kabir.khan@jboss.com" wrote:

    I have done this for https://jira.jboss.org/jira/browse/JBDEPLOY-219

    I think the KCCCreator should be order-able.
    So you don't depend on the order of deployment.

    e.g. XKCCC might be more general than YKCCC,
    but by pure luck XKCCC is added before YKCCC, so YKCCC will never be checked

    ps: add some simple javadocs to createControllerContext + make it protected ;-)

  • 5. Re: AS weld-int
    Kabir Khan Master

     

    "alesj" wrote:
    "kabir.khan@jboss.com" wrote:

    a) I need the BeanManager. BootstrapBean is initialised, so I can call BB.getWeldManager(). I do however need access to the bean manager to use, which I can get from the (Flat)Deployment attachment. I do see some issues down the line if a deployment has several bean managers, but want to get something up and running before digging into that.

    This is the name under which BootstrapBean is registered in MC:
    * String bootstrapName = DeployersUtils.getBootstrapBeanName(unit);


    I have a chicken and egg situation :-) When creating WeldKernelControllerContexts in the BeanMetaDataDeployer plugin, I need a reference to the BeanManager:

     public KernelControllerContext createContext(DeploymentUnit unit, BeanMetaData beanMetaData)
     {
     if (determineWeldDeployment(unit))
     {
     BeanManager beanManager = ????;
     return new WeldKernelControllerContext(null, beanMetaData, null, beanManager);
     }
     return null;
     }
    


    However, since everything is deployed by the BeanMetaDataDeployer there is not really a way to make sure that we are deploying the BeanManager. Unless you know of a nicer way I think I'll end up doing something along the lines of:
     public KernelControllerContext createContext(DeploymentUnit unit, BeanMetaData beanMetaData)
     {
     if (determineWeldDeployment(unit))
     {
     if (isBootstrapBean(beanMetaData))
     return new KernelControllerContext(null, beanMetaData, null);
    
     //Add dependency on bootstrap bean in beanMetaData
     //bootstrap bean should be CREATED, and is needed in the DESCRIBE state *)
     BeanManager beanManager = new DelegatingBeanManager(DeployersUtils.getBootstrapBeanName(unit));
    
     new WeldKernelControllerContext(null, beanMetaData, null, beanManager);
     }
     return null;
     }
    


    *) The DESCRIBE state is probably too early, so I will probably need a WELD_EXTRA_DESCRIBE state or something like that to initialise the WeldInjector for WeldKernelControllerContexts, which I am currently doing as part of the DESCRIBE state.

    public class DelegatingBeanManager implements BeanManager
    {
     private volatile BeanManager manager;
    
     Controller controller;
    
     String beanManagerName;
    
     public DelegatingBeanManager(Controller controller)
     {
     this.controller = controller;
     if (controller instanceof ControllerStateModel == false)
     throw new IllegalArgumentException("Controller is not an instance of ControllerStateModel");
     }
    
     private BeanManager getBeanManager()
     {
     if (manager == null)
     {
     ControllerContext context = controller.getContext(beanManagerName, null);
     if (context == null)
     throw new IllegalStateException("No bean manager found under " + beanManagerName);
    
     manager = (BeanManager)context.getTarget();
     }
     return manager;
     }
    
     /**
     * @param bean
     * @deprecated
     * @see javax.enterprise.inject.spi.BeanManager#addBean(javax.enterprise.inject.spi.Bean)
     */
     public void addBean(Bean<?> bean)
     {
     getBeanManager().addBean(bean);
     }
    
     //Rest of BeanManager methods
    }
    



  • 6. Re: AS weld-int
    Ales Justin Master

    Yeah, something in this "delegate" direction.

    I would simply install a new temp bean,
    which gets BeanManager injected at a proper state.
    Once this injection happens, you deploy a new WKCC and remove the old temp bean.

    Too much of a hack?

  • 7. Re: AS weld-int
    Kabir Khan Master

     

    "alesj" wrote:
    Yeah, something in this "delegate" direction.

    I would simply install a new temp bean,
    which gets BeanManager injected at a proper state.


    I've had some success with this. The main stumbling block at the moment is that each of my tests run in isolation is fine, but if I try to run them all I get an error on next test setup:
    java.lang.IllegalStateException: Incompletely deployed:
    
    DEPLOYMENTS MISSING DEPENDENCIES:
     Deployment "vfs://top-level.ear/BootstrapBeanInstaller=SimpleBean" is missing the following dependencies:
     Dependency "vfs://top-level.ear/_JBossDeployment" (should be in state "Installed", but is actually not found)
     Dependency "vfs://top-level.ear/_WeldBootstrapBean" (should be in state "Create", but is actually not found)
    
     at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.internalValidate(AbstractKernelDeployer.java:278)
     at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.validate(AbstractKernelDeployer.java:174)
     at org.jboss.test.kernel.junit.MicrocontainerTestDelegate.validate(MicrocontainerTestDelegate.java:262)
     at org.jboss.test.kernel.junit.MicrocontainerTest.afterSetUp(MicrocontainerTest.java:117)
     at org.jboss.test.kernel.junit.MicrocontainerTest.setUp(MicrocontainerTest.java:91)
     at org.jboss.test.deployers.BootstrapDeployersTest.setUp(BootstrapDeployersTest.java:350)
     at org.jboss.test.deployers.test.AbstractWeldTest.setUp(AbstractWeldTest.java:59)
    


    'vfs://top-level.ear/BootstrapBeanInstaller=SimpleBean' is the "intermediate" bean that installs SimpleBean once BootstrapBean is installed. How to make sure it gets uninstalled along with the deployment? I tried adding it as an attachment in the WeldKernelControllerContextCreator (used by BeanMetaDataDeployer) which creates this bean but it is not uninstalled.

    To summarize, I have a bean called SimpleBean. BMDDeployer + WeldKernelControllerContextCreator install 'vfs://top-level.ear/BootstrapBeanInstaller=SimpleBean' instead, and this in turn installs SimpleBean in its start() method. On uninstall BMDDeployer.undeploy() gets called with SimpleBean, but not with 'vfs://top-level.ear/BootstrapBeanInstaller=SimpleBean'.

    I will see if I can make 'vfs://top-level.ear/BootstrapBeanInstaller=SimpleBean' uninstall itself, but it feels a bit strange to do that from its start() method.

  • 8. Re: AS weld-int
    Kabir Khan Master

     

    "kabir.khan@jboss.com" wrote:

    I will see if I can make 'vfs://top-level.ear/BootstrapBeanInstaller=SimpleBean' uninstall itself, but it feels a bit strange to do that from its start() method.


    It leaves the context in an inconsistent state
     public void start() throws Exception
     {
     BeanDeploymentArchive archive = deployment.getBeanDeploymentArchives().iterator().next();
    
     if (deployment.getBeanDeploymentArchives().size() > 1)
     log.warn("More than one bean deployment archives, using the first " + archive);
    
     BeanManager manager = bootstrapBean.getBootstrap().getManager(archive);
     if (manager == null)
     throw new IllegalStateException("Could not find a manager for archive " + null);
    
     WeldKernelControllerContext ctx = new WeldKernelControllerContext(null, beanMetaDataHolder.getBeanMetaData(), null, manager);
    
     try
     {
     context.getController().install(ctx);
     }
     catch(Exception e)
     {
     throw e;
     }
     catch(Throwable t)
     {
     throw new Exception(t);
     }
     finally
     {
     ControllerContext me = context;
     context.getController().uninstall(context.getName());
     System.out.println(me);
     }
     }
    

    context is the context of the bean. The call to uninstall completes successfully, unconfiguring the target and setting it to null. However, when StartStopLifecycleAction completes and ends up back in incrementState() the context is picked up and moved through the remainder of the states.


  • 9. Re: AS weld-int
    Ales Justin Master

    It should be then the job of WeldKCC to cleanup this intermediate bean (IB).

    e.g.
    BMDD + WeldKCCC --> IB
    IB::start --> WeldKCC and add DepItem on WeldKCC for IB to be Installed
    * WeldKCC depends on Installed IB in X
    * WeldKCC uninstalls IB in X+1

    It looks a bit hackish, but that's the cost of tying WeldKCC with BootstrapBean.
    And if I'm not yet too sleepy, this should properly cleanup.

    Actually, there is another cleanup issue to take care of. ;-)
    e.g. WeldKCCC adds IB, but BootstrapBean is never installed (for whatever reason)
    This would leave IB hanging in Controller + you would get error for Controller::uninstall("real-weld-bean-name")

    We should track this mapping as well
    * removing it once WeldKCC is installed and IB removed
    * actually removing IB from Controller instead of WeldKCC if the mapping is still there
    (this would mean that the BB was never installed)

  • 10. Re: AS weld-int
    Kabir Khan Master

     

    "alesj" wrote:
    It should be then the job of WeldKCC to cleanup this intermediate bean (IB).

    e.g.
    BMDD + WeldKCCC --> IB
    IB::start --> WeldKCC and add DepItem on WeldKCC for IB to be Installed
    * WeldKCC depends on Installed IB in X
    * WeldKCC uninstalls IB in X+1

    It looks a bit hackish, but that's the cost of tying WeldKCC with BootstrapBean.
    And if I'm not yet too sleepy, this should properly cleanup.

    I am doing this via an install now. IB's create() and uninstall methods:
     public void create() throws Exception
     {
     BeanMetaDataBuilder builder = BeanMetaDataBuilder.createBuilder(beanMetaDataHolder.getBeanMetaData());
     builder.addInstallWithThis("installCreatedBean", (String)context.getName(), ControllerState.INSTALLED);
    
     WeldKernelControllerContext ctx = new WeldKernelControllerContext(null, beanMetaDataHolder.getBeanMetaData(), null, manager);
    
     try
     {
     context.getController().install(ctx);
     }
     catch(Throwable t)
     {
     throw new Exception(t);
     }
     }
    
     /**
     * Called when the created bean reaches INSTALLED state.
     * Uninstalls the context of this temp bean
     */
     public void installCreatedBean(Object createdBean)
     {
     context.getController().uninstall(context.getName());
     }
    


    I will make the tests more complete with regard to injection between MC and Weld and then look at the other cleanup issue you mention.

  • 11. Re: AS weld-int
    Kabir Khan Master

     

    "alesj" wrote:

    * actually removing IB from Controller instead of WeldKCC if the mapping is still there


    How do you propose doing this? Another plugin in BeanMetaDataDeployer to handle uninstalls? Or could aliases perhaps work?

  • 12. Re: AS weld-int
    Ales Justin Master

     

    "kabir.khan@jboss.com" wrote:

    I am doing this via an install now. IB's create() and uninstall methods:

    Nice hack. :-)
    Make sure you properly document it.


  • 13. Re: AS weld-int
    Ales Justin Master

     

    "kabir.khan@jboss.com" wrote:

    Another plugin in BeanMetaDataDeployer to handle uninstalls?

    No, BMDD should have no knowledge of Weld's hacks.
    This logic should somehow be placed in WeldKCC(C).

    e.g. could be even a static piece of code, as in any case it should be properly cleaned up
    If it's not cleaned up, then this is the problem of MC or how deployments are handled.

    "kabir.khan@jboss.com" wrote:

    Or could aliases perhaps work?

    No.
    Unless you could transparently swap the underlying "pointer" to original,
    which is not the case nor it should be.

    e.g. I would like to get my Weld bean injected into other service,
    but until I actually swap the pointer I could end up with IB --> CCE ;-)


  • 14. Re: AS weld-int
    Kabir Khan Master

     

    "alesj" wrote:
    "kabir.khan@jboss.com" wrote:

    Another plugin in BeanMetaDataDeployer to handle uninstalls?

    No, BMDD should have no knowledge of Weld's hacks.
    This logic should somehow be placed in WeldKCC(C).


    I meant extending the (Weld)KernelControllerContextCreator concept to also handle the uninstall so that the BMDD can call out to them. Something along the lines of:
     @Override
     public void undeploy(DeploymentUnit unit, BeanMetaData deployment)
     {
     for (KernelControllerContextCreator creator : controllerContextCreators)
     {
     if (creator.uninstall(unit, deployment, controller))
     break;
     }
     }
    


    Without that nothing really knows that it should look for the IB, which is registered in MC under a different name from the bean we actually want to install, and try to uninstall that. WKCCC could contain the registry and handle this.

    Alternatively, I might need an alternative KernelControllerContext implementation that installs the IB under the original name. Once that is created, that could install another bean (IB2) whose job it is to uninstall IB and install the real bean under the original name. However, as you say that could lead to people injecting IB instead of the real bean.

1 2 Previous Next