JBoss.org Community Documentation

Chapter 19. Introduction

JBoss Microcontainer at its core allows POJOs to be deployed into a runtime environment to create services. It does this using a state machine to ensure that any dependencies on classloaders or other POJOs are satisfied before progressing through the various stages of creation and configuration. By design the state machine and dependency mechanisms are generic which means that we can also use them to deploy other types of runtime components such as MBeans or OSGi bundles. However this means that we also need a generic deployment framework that is capable of creating the relevant metadata and classloader information in addition to handling nested deployments.

To understand why we cannot simply reuse the BasicXMLDeployer that we are already familiar with let's take a closer look at what it actually does:


public class BasicXMLDeployer extends BasicKernelDeployer
{
   ...

   public KernelDeployment deploy(final URL url) throws Throwable
   {
      final boolean trace = log.isTraceEnabled();

      if (url == null)
         throw new IllegalArgumentException("Null url");

      if (trace)
         log.trace("Parsing " + url);

      long start = System.currentTimeMillis();
   
      Unmarshaller unmarshaller = factory.newUnmarshaller();
      KernelDeployment deployment = (KernelDeployment) unmarshaller.unmarshal(url.toString(), resolver);

      if (deployment == null)
          throw new RuntimeException("The xml " + url + " is not well formed!"); 

      deployment.setName(url.toString());

      if (trace)
      {
         long now = System.currentTimeMillis();
         log.trace("Parsing " + url + " took " + (now-start) + " milliseconds");
      }

      deploy(deployment);

      if (trace)
      {
         long now = System.currentTimeMillis();
         log.trace("Deploying " + url + " took " + (now-start) + " milliseconds");
      }

      return deployment;
   }

   ...
}

Once the deploy() method is called with a URL the first thing we do is create an unmarshaller to convert the XML deployment descriptor into an object representation of type KernelDeployment. We then process the KernelDeployment using the deploy() method of the superclass BasicKernelDeployer:


public class BasicKernelDeployer extends AbstractKernelDeployer
{
    ...

   public void deploy(KernelDeployment deployment) throws Throwable
   {
      ...
      super.deploy(deployment);
      deployments.add(deployment);
      ...
   }

    ...

   public void shutdown()
   {
      ListIterator iterator = deployments.listIterator(deployments.size());
      while (iterator.hasPrevious())
      {
         KernelDeployment deployment = (KernelDeployment) iterator.previous();
         undeploy(deployment);
      }
   }
}

As you can see the BasicKernelDeployer simply calls the deploy() method of its superclass AbstractKernelDeployer before adding the deployment to a list.

Note

The reason for having BasicKernelDeployer is that it contains a shutdown() method which undeploys all the deployments in reverse order. This allows the microcontainer to shutdown gracefully.


public class AbstractKernelDeployer
{
    ...
   public void deploy(final KernelDeployment deployment) throws Throwable
   {
      if (deployment.isInstalled())
         throw new IllegalArgumentException("Already installed " + deployment.getName());

      try
      {
         deployBeans(controller, deployment);
         deployment.setInstalled(true);
      }
      catch (Throwable t)
      {
         undeploy(deployment);
         throw t;
      }
   }

   ...

   protected void deployBeans(KernelController controller, KernelDeployment deployment) throws Throwable
   {
      List<BeanMetaData> beans = deployment.getBeans();
      if (beans != null)
      {
         for (BeanMetaData metaData : beans)
         {
            KernelControllerContext context = deployBean(controller, deployment, metaData);
            deployment.addInstalledContext(context);
         }
      }
   }

   protected KernelControllerContext deployBean(KernelController controller, KernelDeployment deployment, BeanMetaData bean) throws Throwable
   {
      KernelControllerContext context = new AbstractKernelControllerContext(null, bean, null);
      if (requiredState != null)
         context.setRequiredState(requiredState);
      if (mode != null)
         context.setMode(mode);
      // Use any deployment classloader if present and the bean doesn't have one
      ClassLoaderMetaData beanClassLoader = bean.getClassLoader();
      if (beanClassLoader == null && deployment != null)
      {
         ClassLoaderMetaData deploymentClassLoader = deployment.getClassLoader();
         if (deploymentClassLoader != null)
            bean.setClassLoader(deploymentClassLoader);
      }
      controller.install(context);
      return context;
   }

    ...
}

The deploy() method in AbstractKernelDeployer performs a check to see if the deployment has already been installed before calling deployBeans() to split it up into the individual BeanMetaData instances. These are then deployed into the runtime environment using the deployBean() method which creates a KernelControllerContext for each BeanMetaData and installs it into the controller. It also takes care of deciding which classloader to use based on information contained in the KernelDeployment object.

You should hopefully see from this breakdown that the type hierarchy of BasicXMLDeployer means that its only suitable for processing deployments of beans. Each deployment is represented as a KernelDeployment that contains multiple BeanMetaData instances; one per bean. If we want to deploy other components such as MBeans or OSGi bundles then we need to deal with other types of metadata and we need a generic way to represent deployments containing them. Furthermore we need to find a flexibile way to process these deployments since each would have its own set of actions that need to take place. For example MBeans need to be registered with an MBean server before they can be considered 'deployed'.

It turns out that we can use the state machine to meet these requirements by creating a DeploymentControllerContext to represent a deployment in the same way that a KernelControllerContext represents a POJO. This allows us to associate a number of deployment actions to different stages of the deployment process:

  • PARSE - convert one or more deployment descriptors into object representations

  • DESCRIBE - determine any dependencies

  • CLASSLOADER - create classloaders

  • POST_CLASSLOADER - apply AOP changes together with any other bytecode manipulations

  • REAL - deploy components (POJO, MBeans or OSGi bundles) into the runtime environment

As the state machine controller takes a DeploymentControllerContext through each of these stages it calls the associated actions to perform the relevant work. For example we could create multiple parsing actions; one to parse XML files and another to parse Java property files. Depending on the file suffix of the deployment descriptor; .xml or .property we can then choose which one to use during the PARSE stage.

As deployments can often be nested within one another we actually describe the details of each deployment using a DeploymentContext type. The DeploymentControllerContext therefore contains a reference to the top-level DeploymentContext:


public class DeploymentControllerContext extends AbstractControllerContext
{
   /** The deployment context */
   private DeploymentContext deploymentContext;
   
   public DeploymentControllerContext(DeploymentContext context, DeployersImpl deployers)
   {
      super(context.getName(), deployers);
      this.deploymentContext = context;
      setMode(ControllerMode.MANUAL);
   }

   public DeploymentContext getDeploymentContext()
   {
      return deploymentContext;
   }
}

Warning

Be careful not to get confused with the names of these types; DeploymentControllerContext and DeploymentContext, as they are very similar.

The deployment framework is therefore a collection of packages and classes that provides a generic way to represent deployments containing components. Deployments can be nested within one another and the components can be anything from POJOs to MBeans and OSGi bundles.

Consequently this means that it's possible to use the deployment framework instead of the BasicXMLDeployer to deploy POJO-based services specified with XML deployment descriptors. So why then do we need a BasicXMLDeployer? The answer is because the deployment framework itself a POJO-based service specified with an XML deployment descriptor (usually called bootstrap-beans.xml ). We therefore need the BasicXMLDeployer to deploy it when the microcontainer is booted up. Once deployed then the framework is used to deploy everything else including any other POJO-based services.

Now that you understand the reasoning for the deployment framework and how it relates to the BasicXMLDeployer the following sections go on the cover the various parts in more detail.