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:
Distribute
Start
Stop
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.
Comments