<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<body link="#355491" alink="#4262a1" vlink="#355491" style="background: #e2e2e2; margin: 0; padding: 20px;">

<div>
        <table cellpadding="0" bgcolor="#FFFFFF" border="0" cellspacing="0" style="border: 1px solid #dadada; margin-bottom: 30px; width: 100%; -moz-border-radius: 6px; -webkit-border-radius: 6px;">
                <tbody>
                        <tr>

                                <td>

                                        <table border="0" cellpadding="0" cellspacing="0" bgcolor="#FFFFFF" style="border: solid 2px #ccc; background: #dadada; width: 100%; -moz-border-radius: 6px; -webkit-border-radius: 6px;">
                                                <tbody>
                                                        <tr>
                                                                <td bgcolor="#000000" valign="middle" height="58px" style="border-bottom: 1px solid #ccc; padding: 20px; -moz-border-radius-topleft: 3px; -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 5px; -webkit-border-top-left-radius: 5px;">
                                                                        <h1 style="color: #333333; font: bold 22px Arial, Helvetica, sans-serif; margin: 0; display: block !important;">
                                                                        <!-- To have a header image/logo replace the name below with your img tag -->
                                                                        <!-- Email clients will render the images when the message is read so any image -->
                                                                        <!-- must be made available on a public server, so that all recipients can load the image. -->
                                                                        <a href="http://community.jboss.org/index.jspa" style="text-decoration: none; color: #E1E1E1">JBoss Community</a></h1>
                                                                </td>

                                                        </tr>
                                                        <tr>
                                                                <td bgcolor="#FFFFFF" style="font: normal 12px Arial, Helvetica, sans-serif; color:#333333; padding: 20px;  -moz-border-radius-bottomleft: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px;"><h3 style="margin: 10px 0 5px; font-size: 17px; font-weight: normal;">
    Asynchronous results from executing a deployment plan
</h3>
<span style="margin-bottom: 10px;">
    created by <a href="http://community.jboss.org/people/bstansberry%40jboss.com">Brian Stansberry</a> in <i>JBoss AS7 Development</i> - <a href="http://community.jboss.org/message/560731#560731">View the full discussion</a>
</span>
<hr style="margin: 20px 0; border: none; background-color: #dadada; height: 1px;">

<div class="jive-rendered-content"><p>One of the main things I've been struggling with when implementing deployments is how to provide information to the caller on the results of executing a deployment plan. In the deployment API discussed at <a class="jive-link-external-small" href="https://community.jboss.org/thread/155937?tstart=0">https://community.jboss.org/thread/155937?tstart=0</a> the StandaloneDeploymentPlan.execute(DeploymentPlan) method returns a DeploymentPlanResult object. That's straightforward enough; what's interesting is making getting the details of those results asynchronous.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>There are two facets of the asynchronous problem:</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>1) As discussed on <a class="jive-link-external-small" href="https://community.jboss.org/thread/154922?tstart=0">https://community.jboss.org/thread/154922?tstart=0</a> there's a desire to immediately return control to the user and let them come back later to check for results. I'm looking at doing that by encapsulating the result details in a Future and executing the plan on another thread. Simple enough.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>2) Even if we made the caller always block waiting for the results, it's still complex, because the deployment process itself is asynchronous. More specifically, installing the services generated from the deployment is multi-threaded -- MSC breaks down all service start/stop work into tasks that are executed by threads from an Executor. So, the thread that's executing the deployment plan and trying to assemble the results can't just invoked BatchBuilder.install() and assume everything is done when that call return. After install() returns that thread needs to find a way to detect what all the services associated with the deployment are and monitor their status as other threads actually register and start them.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>The 2) issue is the focus of the rest of this post.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>The approach I'm looking at using is making use of the o.j.as.deployment.DeploymentService class to facilitate this. Currently that class is being used as a sort of empty placeholder on which all other services associated with a deployment depend. Telling the MSC to stop/remove a DeploymentService instance is thus a simple way to trigger removal of all the associated services. This is how undeploy and rollback of a failed deployment are working.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>What I'm looking at doing is giving DeploymentService a richer set of behaviors. Basically giving it the ability to track what the services are that were associated with a deployment and an API that lets callers find out about those services. Users interested in finding out details of the results of executing a deployment plan could indirectly call into that API.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>The DeploymentService learns about the services associated with a deployment by getting callbacks from a ServiceListener that is registered with the sub-batch that's actually doing the deployment:</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><pre class="jive-pre"><code class="jive-code jive-java"><font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> activate(<font color="navy"><b>final</b></font> ServiceActivatorContext context) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; .......
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>final</b></font> BatchBuilder batchBuilder = context.getBatchBuilder();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="darkgreen">// Create deployment service</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>final</b></font> ServiceName deploymentServiceName = DeploymentService.SERVICE_NAME.append(deploymentName);
&#160;&#160;&#160;&#160;&#160;&#160;&#160; DeploymentService deploymentService = <font color="navy"><b>new</b></font> DeploymentService();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; batchBuilder.addService(deploymentServiceName, deploymentService);
&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="darkgreen">// Create a sub-batch for this deployment</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>final</b></font> BatchBuilder deploymentSubBatch = batchBuilder.subBatchBuilder();
&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="darkgreen">// Setup a batch level dependency on deployment service</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; deploymentSubBatch.addDependency(deploymentServiceName);
&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="darkgreen">// Let deploymentService listen to services in the subbatch</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; deploymentSubBatch.addListener(deploymentService.getDependentStartupListener());
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="darkgreen">// Add a deployment failure listener to the batch</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; deploymentSubBatch.addListener(<font color="navy"><b>new</b></font> DeploymentFailureListener(deploymentServiceName));
&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; ..... go on and create deployment unit and pass it to deployer chain
</code></pre><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>Important (i.e. new) bit is in bold.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>The DeploymentService (and the listener class used above) look like this:</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><pre class="jive-pre"><code class="jive-code jive-java"><font color="navy"><b>public</b></font> <font color="navy"><b>class</b></font> DeploymentService <font color="navy"><b>implements</b></font> Service&lt;DeploymentService&gt; <font color="navy">{</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>static</b></font> <font color="navy"><b>final</b></font> ServiceName SERVICE_NAME = ServiceName.JBOSS.append(<font color="red">"deployment"</font>);
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>static</b></font> Logger logger = Logger.getLogger(<font color="red">"org.jboss.as.deployment"</font>);
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>final</b></font> Map&lt;ServiceName, ServiceController&lt;?&gt;&gt; dependents = <font color="navy"><b>new</b></font> HashMap&lt;ServiceName, ServiceController&lt;?&gt;&gt;();
&#160;&#160;&#160; <font color="darkgreen">/** Dependent services that have not yet reached a terminal state in their initial startup (UP, FAILED, DOWN, REMOVED) */</font>
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>final</b></font> Set&lt;ServiceName&gt; incompleteDependents = <font color="navy"><b>new</b></font> HashSet&lt;ServiceName&gt;();
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>final</b></font> Lock lock = <font color="navy"><b>new</b></font> ReentrantLock();
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>final</b></font> Condition startupCondition = lock.newCondition();
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>final</b></font> Condition stoppedCondition = lock.newCondition();
&#160;&#160;&#160; <font color="darkgreen">/** Whether start() has been invoked since initialization or the last stop() call */</font>
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>boolean</b></font> started = <font color="navy"><b>false</b></font>;
&#160;&#160;&#160; <font color="darkgreen">/** Whether stop() has been invoked since initialization or the last start() call */</font>
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>boolean</b></font> stopped = <font color="navy"><b>false</b></font>;
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Start the deployment.&#160; This will re-mount the deployment root if service is restarted.
&#160;&#160;&#160;&#160; *
&#160;&#160;&#160;&#160; * @param context The start context
&#160;&#160;&#160;&#160; * @throws StartException if any problems occur
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> start(StartContext context) <font color="navy"><b>throws</b></font> StartException <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; started = <font color="navy"><b>true</b></font>;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; stopped = <font color="navy"><b>false</b></font>;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; startupCondition.notifyAll();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; <font color="navy">}</font>
&#160;
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Stop the deployment.&#160; This will close the virtual file mount.
&#160;&#160;&#160;&#160; * 
&#160;&#160;&#160;&#160; * @param context The stop context
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> stop(StopContext context) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; stopped = <font color="navy"><b>true</b></font>;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; started = <font color="navy"><b>false</b></font>;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; stoppedCondition.notifyAll();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; <font color="navy">}</font>
&#160;
&#160;&#160;&#160; <font color="darkgreen">/** {@inheritDoc} **/</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> DeploymentService getValue() <font color="navy"><b>throws</b></font> IllegalStateException <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>return</b></font> <font color="navy"><b>this</b></font>;
&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Blocks until all services associated with this deployment have
&#160;&#160;&#160;&#160; * completed startup (not necessarily successfully).
&#160;&#160;&#160;&#160; * 
&#160;&#160;&#160;&#160; * @throws InterruptedException
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> awaitDependentStartup() <font color="navy"><b>throws</b></font> InterruptedException <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>while</b></font> (!stopped &amp;&amp; (!started || incompleteDependents.size() &gt; 0)) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>if</b></font> (onlyNeverMode()) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>break</b></font>;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; startupCondition.await();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Blocks until all services associated with this deployment have
&#160;&#160;&#160;&#160; * completed startup (not necessarily successfully) or the specified
&#160;&#160;&#160;&#160; * timeout occurs.
&#160;&#160;&#160;&#160; * 
&#160;&#160;&#160;&#160; * @throws InterruptedException
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> awaitDependentStartup(<font color="navy"><b>long</b></font> timeout, TimeUnit timeUnit) <font color="navy"><b>throws</b></font> InterruptedException <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>while</b></font> (!stopped &amp;&amp; (!started || incompleteDependents.size() &gt; 0)) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>if</b></font> (onlyNeverMode()) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>break</b></font>;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; startupCondition.await(timeout, timeUnit);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Blocks until this service is stopped.
&#160;&#160;&#160;&#160; * 
&#160;&#160;&#160;&#160; * @throws InterruptedException
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> awaitStop() <font color="navy"><b>throws</b></font> InterruptedException <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>while</b></font> (!stopped) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; stoppedCondition.await();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Blocks until this service is stopped or the specified
&#160;&#160;&#160;&#160; * timeout occurs.
&#160;&#160;&#160;&#160; * 
&#160;&#160;&#160;&#160; * @throws InterruptedException
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> awaitStop(<font color="navy"><b>long</b></font> timeout, TimeUnit timeUnit) <font color="navy"><b>throws</b></font> InterruptedException <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>while</b></font> (!stopped) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; stoppedCondition.await(timeout, timeUnit);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="navy">}</font>
&#160;
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Gets any exceptions that occurred during start of the services that
&#160;&#160;&#160;&#160; * are associated with this deployment.
&#160;&#160;&#160;&#160; * 
&#160;&#160;&#160;&#160; * @return the exceptions keyed by the name of the service. Will not be &lt;code&gt;null&lt;/code&gt;
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> Map&lt;ServiceName, StartException&gt; getDependentStartupExceptions() <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Map&lt;ServiceName, StartException&gt; result = <font color="navy"><b>new</b></font> HashMap&lt;ServiceName, StartException&gt;();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>for</b></font> (Map.Entry&lt;ServiceName, ServiceController&lt;?&gt;&gt; entry : dependents.entrySet()) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; StartException se = entry.getValue().getStartException();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>if</b></font> (se != <font color="navy"><b>null</b></font>)
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; result.put(entry.getKey(), se);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>return</b></font> result;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Gets the {@link ServiceController.State state} of the services that
&#160;&#160;&#160;&#160; * are associated with this deployment.
&#160;&#160;&#160;&#160; * 
&#160;&#160;&#160;&#160; * @return the services and their current state. Will not be &lt;code&gt;null&lt;/code&gt;
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> Map&lt;ServiceName, ServiceController.State&gt; getDependentStates() <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Map&lt;ServiceName, ServiceController.State&gt; result = <font color="navy"><b>new</b></font> HashMap&lt;ServiceName, ServiceController.State&gt;(dependents.size());
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>for</b></font> (Map.Entry&lt;ServiceName, ServiceController&lt;?&gt;&gt; entry : dependents.entrySet()) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; result.put(entry.getKey(), entry.getValue().getState());
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>return</b></font> result;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="darkgreen">/**
&#160;&#160;&#160;&#160; * Gets a {@link ServiceListener} that can track startup events for 
&#160;&#160;&#160;&#160; * services associated with the deployment this service represents. This 
&#160;&#160;&#160;&#160; * listener should
&#160;&#160;&#160;&#160; * be associated with a {@link BatchBuilder#subBatchBuilder() sub-batch}
&#160;&#160;&#160;&#160; * of this services batch that encapsulates the creation of services that
&#160;&#160;&#160;&#160; * are associated with the deployment.
&#160;&#160;&#160;&#160; * 
&#160;&#160;&#160;&#160; * @return the service listener
&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160; <font color="navy"><b>public</b></font> ServiceListener&lt;Object&gt; getDependentStartupListener() <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>return</b></font> <font color="navy"><b>new</b></font> DependentServiceListener();
&#160;&#160;&#160; <font color="navy">}</font>
&#160;
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="darkgreen">/** Checks whether all incomplete dependents are Mode.NEVER. Must be called with the lock held */</font>
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>boolean</b></font> onlyNeverMode() <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>int</b></font> ever = incompleteDependents.size();
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>for</b></font> (ServiceName name : incompleteDependents) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ServiceController&lt;?&gt; controller = dependents.get(name);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>if</b></font> (controller == <font color="navy"><b>null</b></font> || controller.getMode() == Mode.NEVER)
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ever--;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>return</b></font> ever == 0;
&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="navy"><b>private</b></font> <font color="navy"><b>class</b></font> DependentServiceListener <font color="navy"><b>extends</b></font> AbstractServiceListener&lt;Object&gt; <font color="navy">{</font>
&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="darkgreen">/** 
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; * This will be called for all dependent services before the 
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; * BatchBuilder.install() call returns. So at that point we know what
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; * the dependent services are; other threads will invoke the other
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; * callbacks are services are started.
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; */</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Override
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> listenerAdded(ServiceController&lt;? <font color="navy"><b>extends</b></font> Object&gt; controller) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; dependents.put(controller.getName(), controller);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; incompleteDependents.add(controller.getName());
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Override
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> serviceFailed(ServiceController&lt;? <font color="navy"><b>extends</b></font> Object&gt; controller, StartException reason) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>try</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; incompleteDependents.remove(controller.getName());
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; startupCondition.notifyAll();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Override
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> serviceRemoved(ServiceController&lt;? <font color="navy"><b>extends</b></font> Object&gt; controller) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; try&#160; <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; incompleteDependents.remove(controller.getName());
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; startupCondition.notifyAll();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Override
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>public</b></font> <font color="navy"><b>void</b></font> serviceStopped(ServiceController&lt;? <font color="navy"><b>extends</b></font> Object&gt; controller) <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.lock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; try&#160; <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; incompleteDependents.remove(controller.getName());
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; startupCondition.notifyAll();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy"><b>finally</b></font> <font color="navy">{</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; lock.unlock();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 
&#160;&#160;&#160; <font color="navy">}</font>
&#160;&#160;&#160; 
<font color="navy">}</font>
</code></pre><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>Besides the listener, the other interesting bit in the above are the awaitXXX methods. Those are what allow a caller that actually wants to find out what happened with a deployment to block until the asynchronous MSC tasks complete.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>The awaitStop() methods are straightforward enough.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>The awaitDependentStartup() implementation is more subtle. It depends on the fact that the listener's listenerAdded() method should be invoked passing in any services associated with the deployment before the BatchBuilder.install() method returns. My understanding of how MSC works tells me this is the case -- all listeners associated with a batch are passed to ServiceBuilderImpl and the listenerAdded method is invoked as part of ServiceBuilderImpl.doCreate(). This is all done as part of executing BatchBuilder.install(). This seems like a logical and necessary part of the BatchBuilder.install() contract; it would be good if it were documented as such.</p><p style="min-height: 8pt; height: 8pt; padding: 0px;">&#160;</p><p>There is a subtle race here though. In BatchBuilder.install() the DeploymentService itself could have all dependencies satisfied and tasks executed by another thread to start it before the thread executing install() processes the dependent services and calls listenerAdded(). <span> :( </span> If a caller invoked awaitDependentStartup() during this window, it would return even though the dependent services are not yet started. I'm dealing with that by having the thread that executes BatchBuilder.install() not expose DeploymentService.awaitDependentStartup() to any calling threads until the install() method returns.</p></div>

<div style="background-color: #f4f4f4; padding: 10px; margin-top: 20px;">
    <p style="margin: 0;">Reply to this message by <a href="http://community.jboss.org/message/560731#560731">going to Community</a></p>
        <p style="margin: 0;">Start a new discussion in JBoss AS7 Development at <a href="http://community.jboss.org/choose-container!input.jspa?contentType=1&containerType=14&container=2225">Community</a></p>
</div></td>
                        </tr>
                    </tbody>
                </table>


                </td>
            </tr>
        </tbody>
    </table>

</div>

</body>
</html>