12 Replies Latest reply: Mar 24, 2010 1:07 PM by Marco Bazzoni RSS

JBoss 5 custom deployers and classloaders

Marco Bazzoni Newbie

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
    Ales Justin Master

    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.

  • 2. Re: JBoss 5 custom deployers and classloaders
    Marco Bazzoni Newbie

    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
    Ales Justin Master

    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
    Marco Bazzoni Newbie

    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
    Ales Justin Master
    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
    Marco Bazzoni Newbie

    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
    Adrian Brock Master

    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.

  • 8. Re: JBoss 5 custom deployers and classloaders
    Adrian Brock Master

    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
    Adrian Brock Master

    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
    Marco Bazzoni Newbie

    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
    Adrian Brock Master

    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.

  • 12. Re: JBoss 5 custom deployers and classloaders
    Marco Bazzoni Newbie

    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.