12 Replies Latest reply on Mar 24, 2010 1:07 PM by thebaz

    JBoss 5 custom deployers and classloaders

      To all JBoss gurus,

      I'm facing following problem. I have an Entreprise application that shall have this functionality:

      1. The End user shall upload a text file written with a specified grammar
      2. The application shall parse uploaded file, from this file generate a java source file according to text file specifications and the compile it to obtain a Java Bean (Pojo style), create a jar file containing the newly generated class
      3. Once the jar is generated it shall be automatically added to EAR classpath to be used by the application it self
      4. If Jboss restarts all generated jars must be delpoyed and loaded by the application server

       

      What I did is:

      1. I created a custom deployer that can intercept all jars containing a special XML file in its META-INF folder, just as all jars my Enterprise application generates.
      2. I placed the interceptable jar into deploy directory
      3. The custom deployer intercepts a jar it allocates a classloader (according to jboss deployment life-cycle)

       

      How can I tell the custom deployer to make the class, contained into the jar, be visible to EAR application? Am I following the right path? Is there a code sample that does a similar work? Is this problem solvable?

       

      Hope some guru can help me.

       

      Marco

        • 1. Re: JBoss 5 custom deployers and classloaders
          alesj

          How can I tell the custom deployer to make the class, contained into the jar, be visible to EAR application? Am I following the right path? Is there a code sample that does a similar work? Is this problem solvable?

          If EAR is not scoped you should already see this jar's classes.

          If it is scoped, you need to "attach" the jar to this EAR's cl domain.

          1 of 1 people found this helpful
          • 2. Re: JBoss 5 custom deployers and classloaders

            Thanks Ales,

            If EAR is not scoped you should already see this jar's classes.

            Correct, I see class of an external jar deployed

            If it is scoped, you need to "attach" the jar to this EAR's cl domain.

            It is not clear to me where/when attach the jar to EAR's classloader domain, it must be done by the custom deployer? Can you privide a snippet of code or a sample to do this?

             

            Thank-you in advance,

            Marco.

            • 3. Re: JBoss 5 custom deployers and classloaders
              alesj

              If it is scoped, you need to "attach" the jar to this EAR's cl domain.

              It is not clear to me where/when attach the jar to EAR's classloader domain, it must be done by the custom deployer? Can you privide a snippet of code or a sample to do this?

              While you create "artificial" jar, you simply need to add proper ClassLoadingMetaData --> jboss-classloading.xml.

              * http://java.dzone.com/articles/jboss-microcontainer-classloading

              • 4. Re: JBoss 5 custom deployers and classloaders

                Thanks Ales,

                that was perfect. I got the point.

                 

                Now I'm focusing on a sligthly different think:

                 

                I have my deployers. First one parses XML file and get meta-data:

                 

                public class MibArchiveDeployer extends JAXBDeployer<MibDeployment> {
                    private static Logger log = Logger.getLogger(MibArchiveDeployer.class);
                    /**
                     * Create a new MibArchiveDeployer.
                     */
                    public MibArchiveDeployer() {
                        super(MibDeployment.class);
                        setSuffix("-mib.xml");
                        // Enable the super class ManagedObjectCreator implementation
                        setBuildManagedObject(true);
                        setAllowMultipleFiles(true);
                    }
                
                    /**
                     * Parse a deployment
                     *
                     * @param unit the deployment unit
                     * @param file the metadata file
                     * @param root - possibly null pre-existing root
                     * @return the metadata
                     * @throws Exception for any error
                     */
                    @Override
                    protected MibDeployment parse(VFSDeploymentUnit unit, VirtualFile file, MibDeployment root) 
                          throws Exception {
                        MibDeployment mibDeployment = super.parse(unit, file, root);
                        log.info(format("MibDeployment found: %s", mibDeployment.toString()));
                
                        return mibDeployment;
                    }
                }
                

                 

                Second one which actually deploys the jar archive:

                 

                public class SnmpBeanDeployer extends AbstractSimpleRealDeployer<MibDeployment> {
                    private static Logger log = Logger.getLogger(SnmpBeanDeployer.class);
                    /** The service controller */
                    private final ServiceController serviceController;
                
                    /**
                     * Create a new SnmpBeanDeployer.
                     * @param serviceController service controller
                     */
                    public SnmpBeanDeployer(ServiceController serviceController) {
                        super(MibDeployment.class);
                        if (serviceController == null)
                           throw new IllegalArgumentException("Null service controller.");
                
                        this.serviceController = serviceController;
                    }
                
                    /**
                     * Deploy a deployment
                     *
                     * @param unit       the unit
                     * @param deployment the attachment
                     * @throws org.jboss.deployers.spi.DeploymentException
                     *          for any error
                     */
                    @Override
                    public void deploy(DeploymentUnit unit, MibDeployment deployment) 
                          throws DeploymentException {
                        try {
                            AbstractVFSDeploymentUnit vfsDeploymentUnit = (AbstractVFSDeploymentUnit) unit;
                            VirtualFile jarFile = vfsDeploymentUnit.getRoot();
                            log.info(format("VirtualFile: %s", jarFile));
                            //VirtualFile integration = VFS.getRoot(deployment.getVirtualFileURL());
                            MBeanServer mBeanServer = serviceController.getMBeanServer();
                            log.info(format("MBeanServer: %s", mBeanServer));
                        }
                        catch (Throwable t) {
                            throw DeploymentException.rethrowAsDeploymentException("Error deploying unit.", t);
                        }
                    }
                }
                

                 

                What I want to do is let the second deployer deploy my jar only after an EJB3 present in my EAR is deployed in order to save some info into database.

                 

                Am I right I if re-write parse method of first deployer adding following code?

                 

                    protected MibDeployment parse(VFSDeploymentUnit unit, VirtualFile file, MibDeployment root) 
                          throws Exception {
                        MibDeployment mibDeployment = super.parse(unit, file, root);
                        log.info(format("MibDeployment found: %s", mibDeployment.toString()));
                        ObjectName objectName = new ObjectName(ejbName);
                        unit.addIDependOn(new LifecycleDependencyItem(ejbName, 
                                    objectName.getCanonicalName(), 
                                    ControllerState.START));
                        return mibDeployment;
                    }

                 

                where ejbName is the JDNI name of my EAR's EJB3

                • 5. Re: JBoss 5 custom deployers and classloaders
                  alesj
                  Am I right I if re-write parse method of first deployer adding following code?

                   

                      protected MibDeployment parse(VFSDeploymentUnit unit, VirtualFile file, MibDeployment root) 
                            throws Exception {
                          MibDeployment mibDeployment = super.parse(unit, file, root);
                          log.info(format("MibDeployment found: %s", mibDeployment.toString()));
                          ObjectName objectName = new ObjectName(ejbName);
                          unit.addIDependOn(new LifecycleDependencyItem(ejbName, 
                                      objectName.getCanonicalName(), 
                                      ControllerState.START));
                          return mibDeployment;
                      }
                  

                   

                  where ejbName is the JDNI name of my EAR's EJB3

                  Almost. :-)

                   

                  Currently you're using the ejbName twice, for item owner and its dependency, which is wrong.

                  Check how this is done in jboss-dependency.xml handing code in Deployers -- org.jboss.deployers.vfs.plugins.dependency.

                  * Object contextName = unit.getControllerContextName();

                   

                  Perhaps you can even reuse some of the DeploymentDependencies logic.

                   

                  ps: no need for  LifecycleDependencyItem, plain AbstractDependencyItem will do

                  • 6. Re: JBoss 5 custom deployers and classloaders

                    Hi Ales,

                    I've added in the META-INF folder of the to-be-deployed jar this jboss-depencency.xml file:

                     

                    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
                    <dependency xmlns="urn:jboss:dependency:1.0">
                      <item whenRequired="Real" dependentState="Create">my.domain:service=MyServiceMBean</item>
                    </dependency>
                    

                     

                    And my first deployer parse method source code looks like this:

                     

                    MibDeployment mibDeployment = super.parse(unit, file, root);
                    log.info(format("MibDeployment found: %s", mibDeployment.toString()));
                    ObjectName objectName = new ObjectName(mibMBeanName);
                    Object contextName = unit.getControllerContextName();
                    unit.addIDependOn(new AbstractDependencyItem(contextName,
                         objectName.getCanonicalName(),
                         ControllerState.DESCRIBED,
                         ControllerState.INSTALLED));
                    return mibDeployment;
                    

                     

                    Where mibMBeanName is: my.domain:service=MyServiceMBean

                     

                    But it did not work. Jar is deployed before my MBean is installed.

                     

                    Where's the bug?

                     

                    Thank-you in advance,

                    Marco.

                    • 7. Re: JBoss 5 custom deployers and classloaders

                      You are trying to use ControllerState.DESCRIBED for a deployment which is wrong.

                      That is for determining dependencies for pojos/mbeans etc.

                       

                      You want to use:

                      ControllerState.newState(DeploymentStages.DESCRIBE.getName());

                      which is used for describing classloading dependencies for deployments.

                       

                      NOTE, there is no D on the end, sorry for the confusion in the names. ;-)

                       

                      In fact, ControllerState.DESCRIBED will come after DeploymentStages.REAL

                      which explains why your dependency is not working.

                      It will have already done all the Deployment processing by then.

                      1 of 1 people found this helpful
                      • 8. Re: JBoss 5 custom deployers and classloaders

                        Adrian Brock wrote:

                        You want to use:

                        ControllerState.newState(DeploymentStages.DESCRIBE.getName());

                        which is used for describing classloading dependencies for deployments.

                         

                        NOTE, there is no D on the end, sorry for the confusion in the names. ;-)

                        I guess a better name would have been PRE_CLASSLOADER, but its a bit late to change it now. :-(

                        • 9. Re: JBoss 5 custom deployers and classloaders

                          Marco Bazzoni wrote:


                          And my first deployer parse method source code looks like this:

                           

                          MibDeployment mibDeployment = super.parse(unit, file, root);
                          log.info(format("MibDeployment found: %s", mibDeployment.toString()));
                          ObjectName objectName = new ObjectName(mibMBeanName);
                          Object contextName = unit.getControllerContextName();
                          unit.addIDependOn(new AbstractDependencyItem(contextName,
                               objectName.getCanonicalName(),
                               ControllerState.DESCRIBED,
                               ControllerState.INSTALLED));
                          return mibDeployment;
                          

                           

                          You also don't want to do that in the parse() method. It won't get invoked if somebody passes in the MibDeployment as

                          an attachment (e.g. the management console overriding the metadata) rather than parsing from xml.

                          You should do the extra processing in the init() method which always gets invoked regardless of where the metadata came from.

                          • 10. Re: JBoss 5 custom deployers and classloaders

                            Thank-you Adrian,

                            but method newState in class ControllerState is not present:

                            I use this class org.jboss.dependency.spi.ControllerState in org.jboss.microcontainer:jboss-dependency:2.0.6.GA

                             

                            Wrong dependency?

                             

                            Marco

                            • 11. Re: JBoss 5 custom deployers and classloaders

                              If you are using an older version of jboss-dependency then use

                              new ControllerState(DeploymentStages.DESCRIBE.getName());

                              instead.

                               

                              The newState() method was added later to avoid too many instances getting constructed.

                              1 of 1 people found this helpful
                              • 12. Re: JBoss 5 custom deployers and classloaders

                                Adrian Brock wrote:

                                 

                                If you are using an older version of jboss-dependency then use

                                new ControllerState(DeploymentStages.DESCRIBE.getName());

                                instead.

                                 

                                The newState() method was added later to avoid too many instances getting constructed.

                                Ok, Adrian it worked good. Thank-you to you and Ales for helpfully hints.

                                 

                                Bests,

                                Marco.