Version 3

    Extending the JBoss 3.2.x deployment layer

     

    With the primary focus on improving the functionality and user-friendliness

    of the JBoss management console, we come up with a set of server-side

    requirements

     

    Requirements

     

    • Allow remote deployment of components

    • Allow for starting/stoping of deployments

    • Provide for configuration versioning

    • Provide for persistent configuration

     

    Analysis

     

    JBoss deployment is driven mainly by the MainDeployer with its subdeployers

    (SARDeployer, JARDeployer, etc.) and the various deployment scanners

    (mainly URLDeploymentScanner). Services (and other types of archives) put in the

    ./deploy (or ./farm) directories are automatically deployed, provided the

    specified dependencies are satisfied.

     

    The existing deployment model offers many advantages, including simplicity

    and visibility. The filesystem itself is used as the "user interface" which

    users are accustomed to. There is great flexibility in deploying archives from

    remote locations, specifying new archive types, etc.

     

    Deployment however, is a single step process, i.e. given a URL the main

    deployer will load from its current location (file/url), configure and start a

    deployable service/unit completely. Similarily, undeployment stops and un-loads

    a service/unit from memory. In contrast, the j2ee Deployment API (JSR-88)

    describes a 4-step deployment process:

     

    1. Distribute

    2. Start

    3. Stop

    4. Undeploy

     

    In addition, deploying from remote URLs results in no local copy of the archive.

    (Archives are copied in ./tmp to avoid locking problems, but that's temporary.)

    As a result versioning is not possible.

     

    Finally, the persistence of MBean services seems problematic. MBeans while

    resebling Statuful Session Beans or Entity Beans in their lifecycle, they

    don't have such explicit callback as activate()/passivate() in order to

    prepare for passivation. There is no way also to know if stop/destroy/unregister

    of an MBean means removal of its persistent image. Or, whether creation of an

    MBean should load persistent state or start anew.

     

    Proposal

     

    The existing deployment mechanism of JBoss is proven and well tested, while

    every server subsystem depends on its orderly behaviour. Added to this,

    the imminent re-design of the JBossMicrokernel for the 4.2.x series, we come

    to the conclusion that changes to the existing deployment mechanism

    should not be intrucive, if possible.

     

    Based on this we came up with the idea of a DeployManager service (MBean)

    that would act mostly as an archive handler on top of the existing deployment

    services. The DeployManager will be used by the management console and it could

    be used also as the back-end for the JSR-88 deployment mechanism and as a result

    any JSR-88 based tool.

     

    It's base interface would look something like:

     

       deploy(String archiveId, URL location);
       deploy(String archiveId, byte[] content);
       start(String archiveId);
       stop(String archiveId);
       undeploy(String archiveId);
    

     

    The idea is that DeployManager will have a local directory, parallel to ./conf

    e.g. ./stage. Calls to deploy() should either point to an archive or carry the

    archive content inside a byte array and would result in the deployable unit

    being copied and kept in the ./stage directory, thus simulating the distribute

    primitive of the JSR-88 API.

     

    A call to start() would move the archive in the configured ./deploy directory

    for the Scanner to pick it up. A call to stop() will move back the archive to

    the ./stage directory, and finally, a call to undeploy() would remove the

    archive completely.

     

    Calls to the above methods should be non-blocking. DeployManager should listen

    for events from the MainDeployer to learn about the status of deployments,

    map and propagate those as needed to interested JMX Notification listeners.

     

    ArchiveId could be the file URL as reported by MainDeployer. It could also

    be just the last part of it, e.g. jmx-console.war.

     

    The lifecycle of the deployed archives, as seen from the DeployManager

    should include states like:

     

    • nonExistent

    • inTransit (being copied from remote location)

    • stopped (residing in ./stage)

    • starting(moved to ./deploy, but not started)

    • started(in service)

    • failed(could not start)

    • stopping(moved to ./stage)

     

    Versioning

     

    To version the deployed archives DeployManager could use a storage area,

    e.g. ./data/storage, and have an interface similar to:

     

       load(String configId);
       save(String configId);
       remove(String configId);
       String[] listConfigs();
    

     

    Upon save() it would copy the archives from ./stage ./deploy and possibly

    other designated directories (e.g. data/somedir) to the storage area,

    under a new directory having the same name with the configId.

     

    load() would have to remove all files from the versioned directories

    (./stage, ./deploy) then write back the saved copies.

     

    There are many issues with this approach, however.

     

    One problem during save() and especially if we want to save data dirs, is

    how we know the application is in a safe state to copy it's state? Also

    how we associate some particular data dir with an archive? There is no such

    info with the archive, it would have to be manually entered to the DeployManager,

    e.g. copy ./date/xmbean-state along with ./deploy. Then what if an MBean saves

    configuration state to some external DB or LDAP directory, we wouldn't have

    control over this.

     

    So persistence and versioning problems are related and there must be some

    convention (e.g. all services save configuration state at a certain place)

    and/or all persistent operations should be mediated by a persistence manager,

    but that should be imposed to all services.

     

    load() may be problematic, too, because removing first all files from

    the target ./stage and ./deploy dir, then writing back the saved copies

    requires proper timing. I.e. old deployments must stop completely, before

    we copy the new persistent data in, then deploy the specified version

    of the archives.

     

    In addition, removing and copying back all archives from/to ./deploy may not be

    a good idea, so there is the question of what exactly we want to version.

    An easy way out is to specify a different ./deploy directory for deployments

    coming through the DeployManager. E.g. ./deploy2, or ./deploy-ext, or even

    ./stage/deploy. Doing this we can partition the deployment space into

    "configuration domains".

     

    The server will operate as usual with core services activated/controled through

    the ./deploy dir, while remotely (DeployManager or through JSR-88) deployed

    components would be placed into a different deploy directory. Asked about

    a list of deployed components, the DeployManager could report only those

    components in ./deploy-ext directory.

     

    Farmed Deployments

     

    Moving archives around to perform deployment may not be the best way, but

    it has some nice side-effect. We could add a method like:

     

       startFarmed(String archiveId, String partition, boolean singleton);
    

     

    to move the archive to ./farm, or deploy-hasingleton. In this way there

    is no need to replicate the logic of the cluster and singleton deployers.

     

    Another option is to gather all the DeployManager controlled directories

    into the same hierarchy, e.g.

    ./stage (or ./domain)
    ./stage/deploy
    ./stage/farm
    ./stage/singleton
    

     

    MBean Configuration Persistence

     

    Persisting service configuration using XMBeans presents a few problems.

     

    First it doesn't seem easy to make everything an XMBean (or at least,

    everything that we want to persist). (I had some problems with MBeans

    that generate or consume notifications).

     

    Second, persistence is not really considered in the current service

    lifecycle model. Under "normal circumstances" a CTOR-config-create-start

    would mean a "new" service being created, with the destruction of any

    persistent data for this object, if it existed. Then persistent state should

    be synchronised whenever attributes change, but we should be able to differentiate

    between a service destroy (persistent image being deleted) and a swap-out

    (persistent image retained).

     

    Since a service itself (whether implemented as an XMBean or not) is responsible

    for its persistence, at a minimum, there should be an attribute, e.g.

     

       boolean isPersisted();
    

     

    whose 'true' return value would indicate there is state saved for this MBean,

    so the configuration step should be skipped by the MainDeployer (otherwise,

    the persisted properties are overriden!). Absence of the attribute would

    indicate that the MBean does not support persistence and a 'false' return

    value that the MBean supports persistence, but the image was cleared. In both

    cases, normal configuration should take place at start-up.

     

    Another method would be required if we want to force clearance of the persistent

    state, e.g.

       void clearPersistentState();
    

     

    The mbean deployment descriptor should be possibly enhanced with an option

    similar to "clearPersistentState", to force clearance of the persistent image

    at startup. (Essentially, attempt to call

    clearPersistent()

    if exists.)

     

    The XMBean implementation could be enchanced to implement the above. The

    standard MBeanSupport could be extended too.

     

    Creation of Persistent Services

     

    The most easy way seems to be a custom MBean service that simply acts as

    a factory for deployment descriptors for the most "popular" services.