org.intalio.deploy.deployment.impl.DeploymentServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.intalio.deploy.deployment.impl.DeploymentServiceImpl.java

Source

/**
 * Copyright (c) 2005-2010 Intalio inc.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Intalio inc. - initial API and implementation
 */
package org.intalio.deploy.deployment.impl;

import static org.intalio.deploy.deployment.impl.LocalizedMessages._;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileWriter;
import java.rmi.Remote;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.intalio.deploy.deployment.AssemblyId;
import org.intalio.deploy.deployment.ComponentId;
import org.intalio.deploy.deployment.DeployedAssembly;
import org.intalio.deploy.deployment.DeployedComponent;
import org.intalio.deploy.deployment.DeploymentMessage;
import org.intalio.deploy.deployment.DeploymentMessage.Level;
import org.intalio.deploy.deployment.DeploymentResult;
import org.intalio.deploy.deployment.DeploymentService;
import org.intalio.deploy.deployment.impl.DeployMBeanServer.NullDeployMBeanServer;
import org.intalio.deploy.deployment.impl.clustering.ActivatedMessage;
import org.intalio.deploy.deployment.impl.clustering.Cluster;
import org.intalio.deploy.deployment.impl.clustering.ClusterListener;
import org.intalio.deploy.deployment.impl.clustering.DeployedMessage;
import org.intalio.deploy.deployment.impl.clustering.RetiredMessage;
import org.intalio.deploy.deployment.impl.clustering.SingleNodeCluster;
import org.intalio.deploy.deployment.impl.clustering.UndeployedMessage;
import org.intalio.deploy.deployment.spi.ComponentManager;
import org.intalio.deploy.deployment.spi.ComponentManagerLockAware;
import org.intalio.deploy.deployment.spi.ComponentManagerResult;
import org.intalio.deploy.deployment.spi.DeploymentServiceCallback;
import org.intalio.deploy.registry.RemoteProxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.SystemPropertyUtils;

import com.intalio.bpms.common.node.heath.NodeHealth;

/**
 * Deployment service
 */
@ManagedResource(objectName = "intalio:module=DeploymentService,service=DeploymentService")
public class DeploymentServiceImpl implements DeploymentService, Remote, ClusterListener {
    private static final Logger LOG = LoggerFactory.getLogger(DeploymentServiceImpl.class);

    // Constants
    public static final String DEFAULT_DEPLOY_DIR = "${org.intalio.deploy.configDirectory}/../deploy";

    public static final String DEPLOY_COMPONENT = "DeploymentService";

    public static final String DEFAULT_DATASOURCE_JNDI_PATH = "java:/comp/env/jdbc/BPMSDB";

    public static String DO_NOT_DELETE_FILE_NAME = "doNotDeleteFile";

    //
    // Configuration
    //

    private int _scanPeriod = 5; // in seconds

    private String _deployDir = SystemPropertyUtils.resolvePlaceholders(DEFAULT_DEPLOY_DIR);

    private List<String> _requiredComponentManagers = new ArrayList<String>();

    private String _dataSourceJndiPath = DEFAULT_DATASOURCE_JNDI_PATH;

    //
    // Internal state
    //

    enum ServiceState {
        INITIALIZED, CLUSTERIZING, STARTING, STARTED, STOPPING
    }

    private ServiceState _serviceState;

    /**
     * Mapping of [componentType] to [ComponentManager name] e.g. "BPEL" => "ApacheOde"
     */
    private final Map<String, String> _componentTypes = Collections.synchronizedMap(new HashMap<String, String>());

    /**
     * Mapping of [name] to [DeploymentManager] e.g. "MyApplication" => OdeComponentManager
     */
    private final Map<String, ComponentManager> _componentManagers = Collections
            .synchronizedMap(new HashMap<String, ComponentManager>());

    private final Object LIFECYCLE_LOCK = new Object();

    private ReadWriteLock DEPLOY_LOCK = new ReentrantReadWriteLock();

    private DeploymentServiceCallbackImpl _callback = new DeploymentServiceCallbackImpl();

    //
    // Services
    //

    private Timer _timer;

    private final StartTask _startTask = new StartTask();

    private final TimerTask clusterizeTask = new TimerTask() {
        public void run() {
            try {
                cluster.start();
                onClustered();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    private final ScanTask _scanTask = new ScanTask();

    private DataSource _dataSource;

    private Persistence _persist;

    private boolean replaceExistingAssemblies = false;

    // the Null-Object pattern
    private Cluster cluster = new SingleNodeCluster();

    // the Null-Object pattern
    private DeployMBeanServer _deployMBeanServer = new NullDeployMBeanServer();

    //
    // Constructor
    //

    public DeploymentServiceImpl() {
    }

    //
    // Accessors / Setters
    //

    private void writeLockDeploy() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Locking " + DEPLOY_LOCK);
        }
        DEPLOY_LOCK.writeLock().lock();
        for (ComponentManager c : _componentManagers.values()) {
            tryLock(c, true);
        }
    }

    private void writeUnlockDeploy() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Unlocking " + DEPLOY_LOCK);
        }
        for (ComponentManager c : _componentManagers.values()) {
            tryLock(c, false);
        }
        DEPLOY_LOCK.writeLock().unlock();
    }

    public DeployMBeanServer getDeployMBeanServer() {
        return _deployMBeanServer;
    }

    public void setDeployMBeanServer(DeployMBeanServer deployMBeanServer) {
        _deployMBeanServer = deployMBeanServer;
    }

    public Cluster getCluster() {
        return cluster;
    }

    public void setCluster(Cluster cluster) {
        this.cluster = cluster;
    }

    public String getDeployDirectory() {
        return _deployDir;
    }

    public void setDeployDirectory(String path) {
        _deployDir = SystemPropertyUtils.resolvePlaceholders(path);
    }

    public int getScanPeriod() {
        return _scanPeriod;
    }

    public void setScanPeriod(int scanPeriod) {
        _scanPeriod = scanPeriod;
    }

    public void setReplaceExistingAssemblies(boolean replaceExistingAssemblies) {
        this.replaceExistingAssemblies = replaceExistingAssemblies;
    }

    public void addComponentTypeMapping(String componentType, String componentManager) {
        _componentTypes.put(componentType, componentManager);
    }

    public void removeComponentTypeMapping(String componentType) {
        _componentTypes.remove(componentType);
    }

    public List<String> getRequiredComponentManagers() {
        return _requiredComponentManagers;
    }

    public void setRequiredComponentManagers(List<String> componentManagers) {
        _requiredComponentManagers = componentManagers;
    }

    public void addRequiredComponentManager(String componentManager) {
        _requiredComponentManagers.add(componentManager);
    }

    public void removeRequiredComponentManager(String componentManager) {
        _requiredComponentManagers.remove(componentManager);
    }

    public DeploymentServiceCallback getCallback() {
        return _callback;
    }

    public DataSource getDataSource() {
        return _dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        _dataSource = dataSource;
    }

    public void setDataSourceJndiPath(String dataSourceJndiPath) {
        _dataSourceJndiPath = dataSourceJndiPath;
    }
    //
    // Lifecycle Methods
    //

    /**
     * Initialize the service
     */
    public void init() {
        synchronized (LIFECYCLE_LOCK) {
            if (_serviceState != null) {
                throw new IllegalStateException("Service already initialized");
            }
            if (_dataSource == null) {
                // by this time, if no datasource is set by the setter, use the default one
                try {
                    InitialContext initialContext = new InitialContext();
                    _dataSource = (DataSource) initialContext.lookup(_dataSourceJndiPath);
                } catch (NamingException e) {
                    throw new IllegalStateException("Couldn't find datasource through jndi");
                }
            }

            _persist = new Persistence(new File(_deployDir), _dataSource);
            _timer = new Timer("Deployment Service Timer", true);
            _serviceState = ServiceState.INITIALIZED;
            LOG.info(_("DeploymentService state is now INITIALIZED(replaceExistingAssemblies="
                    + replaceExistingAssemblies + ")."));
        }
    }

    /**
     * Start the service
     */
    public void start() {
        synchronized (LIFECYCLE_LOCK) {
            if (_serviceState != ServiceState.INITIALIZED) {
                throw new IllegalStateException("Service not initialized");
            }
            _serviceState = ServiceState.CLUSTERIZING;
            LOG.info(_("DeploymentService state is now CLUSTERIZING."));
            _timer.schedule(clusterizeTask, 0);
        }
    }

    public void onClustered() {
        synchronized (LIFECYCLE_LOCK) {
            if (_serviceState != ServiceState.CLUSTERIZING) {
                throw new IllegalStateException("Service not in clusterizing mode.");
            }
            _serviceState = ServiceState.STARTING;
            LOG.info(_("DeploymentService state is now STARTING"));
            checkRequiredComponentManagersAvailable();
        }
    }

    /**
     * Return true if service is started.
     */
    public boolean isStarted() {
        synchronized (LIFECYCLE_LOCK) {
            return _serviceState == ServiceState.STARTED;
        }
    }

    /**
     * Stop the service.
     * Acquires a lock over the timer task and cancels it.
     * Closes and disposes each deployment manager.
     */
    public void stop() {
        synchronized (LIFECYCLE_LOCK) {
            //acquire the assemblies first, before you wait to
            //acquire a lock over the timer task.
            //this way you have better chances to get the assemblies
            //before the connection to the DB closes.
            Collection<DeployedAssembly> assemblies = getDeployedAssemblies();
            if (_serviceState == ServiceState.STARTED) {
                _timer.cancel();
                cluster.shutdown();
            }
            _serviceState = ServiceState.STOPPING;
            LOG.info(_("DeploymentService state is now STOPPING"));

            stopAndDispose(assemblies);

            _serviceState = null;
            LOG.info(_("DeploymentService state is now STOPPED"));
        }
    }

    //
    // Operations
    //
    public DeploymentResult deployAssembly(String assemblyName, InputStream zip) {
        return deployAssembly(assemblyName, zip, true);
    }

    /**
     * Deploy a packaged (zipped) assembly
     */
    public DeploymentResult deployAssembly(String assemblyName, InputStream zip, boolean activate) {
        if (LOG.isDebugEnabled())
            LOG.debug("DEPLOYMENT.deployAssembly(" + assemblyName + ", replaceExistingAssemblies="
                    + replaceExistingAssemblies + ", activate=" + activate + ")");

        assertStarted();

        if (assemblyName.indexOf(".") >= 0) {
            Exception except = new Exception(
                    _("Assembly name cannot contain dot character ('.'): {0}", assemblyName));
            LOG.error(except.getMessage());
            return convertToResult(except, newAssemblyId(assemblyName));
        }

        try {
            writeLockDeploy();

            AssemblyId aid = versionAssemblyId(assemblyName);
            if (replaceExistingAssemblies) {
                Collection<DeployedAssembly> deployed = getDeployedAssemblies();
                for (DeployedAssembly da : deployed) {
                    if (da.getAssemblyId().getAssemblyName().equals(aid.getAssemblyName())) {
                        try {
                            stopAndDispose(da.getAssemblyId());
                            DeploymentResult result = undeployAssembly(da);
                            Utils.deleteRecursively(new File(da.getAssemblyDir()));
                            if (!result.isSuccessful()) {
                                return result;
                            }
                        } catch (Exception except) {
                            LOG.error(_("Error while undeploying assembly {0}", da.getAssemblyId()), except);
                            return convertToResult(except, aid);
                        }
                    }
                }
            }

            try {
                setMarkedAsInvalid(aid, _("Deploying {0} ...", aid));

                File assemblyDir = createAssemblyDir(aid);
                DeploymentResult result = null;
                try {
                    Utils.unzip(zip, assemblyDir);
                    result = deployExplodedAssembly(assemblyDir, activate);
                } finally {
                    if (result == null || !result.isSuccessful()) {
                        Utils.deleteRecursively(assemblyDir);
                    }
                }
                return result;
            } catch (Exception except) {
                throw new RuntimeException(except);
            } finally {
                clearMarkedAsInvalid(aid);
            }
        } finally {
            writeUnlockDeploy();
        }
    }

    /**
     * Deploy an exploded assembly
     */
    public DeploymentResult deployExplodedAssembly(File assemblyDir, boolean activate) {
        File parent = assemblyDir.getParentFile();
        while (true) {
            if (parent == null)
                break;
            parent = parent.getParentFile();
            if (_deployDir.equals(parent)) {
                throw new IllegalArgumentException(
                        "Assembly directory must either be direct child of deployment directory, or outside the deployment area: assemblDir="
                                + assemblyDir + " deploymentdir: " + _deployDir);
            }
        }

        AssemblyId aid;
        boolean local = assemblyDir.getParentFile().equals(new File(_deployDir));
        if (local) {
            aid = parseAssemblyId(assemblyDir.getName());
            if (isMarkedAsDeployed(aid)) {
                throw new RuntimeException("Assembly already deployed: " + aid);
            }
        } else {
            if (assemblyDir.getName().indexOf(".") >= 0)
                throw new IllegalArgumentException("Assembly dir cannot contain dot '.' character: " + assemblyDir);
            aid = versionAssemblyId(assemblyDir.getName());
        }

        TemporaryResult results = new TemporaryResult(aid);
        try {
            writeLockDeploy();
            // mark as invalid while we deploy to avoid concurrency issues with scanner
            setMarkedAsInvalid(aid, _("Deploying {0} ...", aid));

            // if assemblyDir is outside deployDir, copy files into deployDir
            if (!local) {
                File d = getAssemblyDir(aid);
                try {
                    Utils.copyRecursively(assemblyDir, d);
                } catch (IOException except) {
                    Utils.deleteRecursively(d);
                    throw new RuntimeException(except);
                }
            }

            List<DeployedComponent> deployed = new ArrayList<DeployedComponent>();

            try {
                _persist.startTransaction();

                /* The protocol has been changed after discussion with Alex.
                 * We now de-activate active versions when the the 'activate' is set to true.
                 * To have multiple active version for a new component manager,
                 * 1. deploy a version with the activate set to false
                 * 2. call active() on the new version
                 */
                try {
                    if (activate) {
                        retire(aid);
                    }
                } catch (Exception e) {
                    LOG.warn("Exeption during retiring old versions to activate the new one:", e);
                }

                try {
                    File[] files = assemblyDir.listFiles();

                    // deploy each component
                    for (File f : files) {
                        if (!f.isDirectory()) {
                            // ignore files at top-level
                            continue;
                        }
                        int dot = f.getName().lastIndexOf('.');
                        if (dot < 0) {
                            // ignore directories without extension (no mapping)
                            continue;
                        }

                        LOG.debug("Deploying " + f.getCanonicalPath());
                        String componentType = f.getName().substring(dot + 1);
                        String componentName = f.getName().substring(0, dot);
                        ComponentId component = new ComponentId(aid, componentName);

                        ComponentManager manager = getComponentManager(componentType);
                        LOG.debug("ComponentManager resolved: " + manager);
                        try {
                            ComponentManagerResult result = manager.deploy(component, f, activate);
                            results.addAll(component, componentType, result.getMessages());
                            // Sometimes, the component manager returns the same resources multiple times in the list
                            // let's take out the extra resource strings, yet keep the order
                            List<String> deployedResources = new ArrayList<String>();
                            for (String resource : result.getDeployedResources()) {
                                if (!deployedResources.contains(resource)) {
                                    deployedResources.add(resource);
                                } else {
                                    LOG.debug(
                                            "Duplicate resource string found from the Component Manager deployment result, skipping: "
                                                    + resource);
                                }
                            }
                            DeployedComponent deployedComponent = new DeployedComponent(component,
                                    f.getAbsolutePath(), componentType, deployedResources);
                            deployed.add(deployedComponent);
                        } catch (Exception except) {
                            String msg = _("Exception while deploying component {0}: {1}", componentName,
                                    except.getLocalizedMessage());
                            results.add(component, componentType, error(msg));
                            LOG.error(msg, except);
                        }
                    }
                } catch (Exception except) {
                    String msg = _("Exception while deploying assembly {0}: {1}", aid,
                            except.getLocalizedMessage());
                    results.add(null, null, error(msg));
                    LOG.error(msg, except);
                }

                if (results.isSuccessful()) {
                    // update persistent state
                    DeployedAssembly assembly = loadAssemblyState(aid);

                    _persist.retire(aid.getAssemblyName());
                    _persist.add(assembly, deployed);

                    _persist.commitTransaction();

                    deployed(assembly, activate);

                    cluster.sendMessage(new DeployedMessage(assembly, activate));

                    initializeAndStart(aid);
                } else {
                    // in case of failure, we undeploy already deployed components
                    for (DeployedComponent dc : deployed) {
                        try {
                            ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                            manager.undeploy(dc.getComponentId(), new File(dc.getComponentDir()),
                                    dc.getDeployedResources());
                        } catch (Exception except) {
                            String msg = _("Exception while undeploying component {0} after failed deployment: {1}",
                                    dc.getComponentId(), except.getLocalizedMessage());
                            results.add(dc, error(msg));
                            LOG.error(msg, except);
                        }
                    }
                    _persist.rollbackTransaction("Deployment errors");
                }

                setMarkedAsDeployed(aid, results.isSuccessful());
                clearMarkedAsInvalid(aid);
            } catch (Throwable e) {
                String msg = _("Unexpected exception {0}: {1}", e.getClass(), e.getMessage());
                results.add(null, null, error(msg));
                _persist.rollbackTransaction(msg);
            }
        } finally {
            writeUnlockDeploy();
        }

        return results.finalResult();
    }

    /**
     * Undeploy an assembly by name
     */
    public DeploymentResult undeployAssembly(AssemblyId aid) {
        if (LOG.isDebugEnabled())
            LOG.debug("DEPLOYMENT.undeployAssembly(" + aid + ")");

        assertStarted();

        if (!exist(aid))
            return errorResult(aid, "Assembly directory does not exist: {0}", aid);

        DeployedAssembly assembly = loadAssemblyState(aid);
        DeployedAssembly assemblyFromDatabase = _persist.load().get(aid);
        if (assemblyFromDatabase != null) {
            assembly = assemblyFromDatabase;
        }

        stopAndDispose(aid);
        if (cluster.isCoordinator()) {
            cluster.sendMessage(new UndeployedMessage(assembly));
        }
        onUndeployed(assembly);

        try {
            writeLockDeploy();
            try {
                return undeployAssembly(assembly);
            } finally {
                try {
                    Utils.deleteRecursively(new File(assembly.getAssemblyDir()));
                } catch (Exception e) {
                    LOG.warn(_("Exception while undeploying assembly {0}: {1}", assembly.getAssemblyId(),
                            e.toString()));
                }
            }
        } finally {
            writeUnlockDeploy();
        }
    }

    /**
     * Scan all exploded assemblies in the deployment directory, and optionally
     * start newly deployed assemblies.
     */
    public void scan() {
        if (!(cluster.isCoordinator() && NodeHealth.isNodeHealthy())) {
            return;
        }
        File deployDir = new File(_deployDir);
        File[] files = deployDir.listFiles();

        LOG.debug(_("Scanning deployment directory {0}", _deployDir));
        LOG.debug(_("Component managers: {0}", _componentManagers));
        try {
            writeLockDeploy();
            Map<AssemblyId, DeployedAssembly> deployedMap = _persist.load();
            LOG.debug(_("Deployed assemblies: {0}", deployedMap.keySet()));

            Set<AssemblyId> available = new HashSet<AssemblyId>();
            Set<AssemblyId> availableWithDeployMark = new HashSet<AssemblyId>();
            // read available assemblies
            if (files != null) {
                for (int i = 0; i < files.length; ++i) {
                    if (files[i].isDirectory()) {
                        AssemblyId aid = parseAssemblyId(files[i].getName());
                        available.add(aid);
                    } else {
                        String name = files[i].getName();
                        if (name.endsWith(".deployed")) {
                            availableWithDeployMark
                                    .add(parseAssemblyId(name.substring(0, name.length() - ".deployed".length())));
                        }
                    }
                }
            }
            LOG.debug(_("Available assemblies on file system: {0}", available));

            // Phase 1: undeploy missing assemblies
            Set<DeployedAssembly> undeploy = new HashSet<DeployedAssembly>();
            // check for previously deployed but now missing
            for (DeployedAssembly assembly : deployedMap.values()) {
                // if helloWorld is removed but helloWorld.deployed is there,
                // then undeploy.
                // if you cannot find both it is possible the file system is in
                // flux
                // and you should not undeploy.
                if (!available.contains(assembly.getAssemblyId())
                        && availableWithDeployMark.contains(assembly.getAssemblyId()))
                    undeploy.add(assembly);
            }

            // check for available but deployed flag missing
            for (AssemblyId aid : available) {
                DeployedAssembly assembly = deployedMap.get(aid);
                if (assembly != null && !isMarkedAsDeployed(aid))
                    undeploy.add(assembly);
            }

            if (!NodeHealth.isNodeHealthy())
                return;
            // stop and dispose all at once
            stopAndDispose(undeploy);

            for (DeployedAssembly assembly : undeploy) {
                DeployedAssembly assemblyFromDatabase = deployedMap.get(assembly.getAssemblyId());
                if (assemblyFromDatabase != null) {
                    assembly = assemblyFromDatabase;
                }

                cluster.sendMessage(new UndeployedMessage(assembly));

                DeploymentResult result = undeployAssembly(assembly);
                if (result.isSuccessful())
                    LOG.info(_("Undeployed assembly: {0}", assembly.getAssemblyId()));
                else
                    LOG.error(_("Error while undeploying assembly {0}: {1}", assembly.getAssemblyId()), result);
                deployedMap.remove(assembly.getAssemblyId());
            }

            // phase 2: deploy new assemblies
            if (!NodeHealth.isNodeHealthy())
                return;
            if (files != null) {
                for (int i = 0; i < files.length; ++i) {
                    if (files[i].isDirectory()) {
                        AssemblyId aid = parseAssemblyId(files[i].getName());
                        if (!isMarkedAsDeployed(aid) && !isMarkedAsInvalid(aid)) {
                            try {
                                // auto-detected assemblies are always activated
                                // after deployment
                                DeploymentResult result = deployExplodedAssembly(files[i], true);
                                if (result.isSuccessful()) {
                                    LOG.info(_("Deployed Assembly: {0}", result));
                                    clearMarkedAsInvalid(aid);
                                } else {
                                    LOG.warn(_("Assembly deployment failed: {0}", result));
                                    setMarkedAsInvalid(aid, result.toString());
                                }
                            } catch (Exception except) {
                                LOG.error(_("Error deploying assembly {0}. Assembly will be marked as invalid.",
                                        files[i]), except);
                                setMarkedAsInvalid(aid, except.toString());
                            }
                        } else if (isMarkedAsDeployed(aid) && !deployedMap.containsKey(aid)) {
                            if (LOG.isWarnEnabled())
                                LOG.warn(_(
                                        "Inconsistent states found between the file system and the deployment service database!!"));
                            if (LOG.isWarnEnabled())
                                LOG.warn(_("A valid assembly for " + aid
                                        + " exists on the file system but missing in the database; You can re-deploy the assembly by removing the .deployed files for the assemblies."));
                        }
                    }
                }
            }
        } finally {
            writeUnlockDeploy();
        }
    }

    /**
     * Obtain the current list of deployed assemblies
     */
    public Collection<DeployedAssembly> getDeployedAssemblies() {
        try {
            writeLockDeploy();
            Map<AssemblyId, DeployedAssembly> assemblies = _persist.load();
            return assemblies.values();
        } finally {
            writeUnlockDeploy();
        }
    }

    public Collection<DeployedAssembly> readDeployedAssemblies() {
        List<DeployedAssembly> assemblies = new ArrayList<DeployedAssembly>();
        try {
            writeLockDeploy();
            File[] files = new File(_deployDir).listFiles();
            for (int i = 0; i < files.length; ++i) {
                if (files[i].isDirectory()) {
                    AssemblyId aid = parseAssemblyId(files[i].getName());
                    if (isMarkedAsDeployed(aid) && !isMarkedAsInvalid(aid)) {
                        try {
                            assemblies.add(loadAssemblyState(aid));
                        } catch (Exception except) {
                            LOG.error(_("Error reading assembly state {0}", aid), except);
                        }
                    }
                }
            }
        } finally {
            writeUnlockDeploy();
        }
        return assemblies;
    }

    //
    // Private / Protected Internal Methods
    //

    /**
     * Undeploy an assembly
     */
    private DeploymentResult undeployAssembly(DeployedAssembly assembly) {
        AssemblyId aid = assembly.getAssemblyId();

        TemporaryResult result = new TemporaryResult(aid);
        try {
            writeLockDeploy();
            try {
                // undeploy all components
                for (DeployedComponent dc : assembly.getDeployedComponents()) {
                    try {
                        ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                        manager.undeploy(dc.getComponentId(), new File(dc.getComponentDir()),
                                dc.getDeployedResources());

                        _deployMBeanServer.unregisterComponent(dc.getComponentManagerName(), dc.getComponentId());
                    } catch (Exception except) {
                        String msg = _("Exception while undeploying component {0}: {1}", dc.getComponentId(),
                                except.getLocalizedMessage());
                        result.add(dc, error(msg));
                        LOG.error(msg, except);
                    }
                }
            } catch (Exception except) {
                String msg = _("Exception while undeploying assembly {0}: {1} ", aid, except.getLocalizedMessage());
                result.add(null, null, error(msg));
                LOG.error(msg, except);
            } finally {
                // update persistent state
                _persist.remove(aid);

                if (!result.isSuccessful() && exist(aid)) {
                    setMarkedAsInvalid(aid, result.toString());
                }
                setMarkedAsDeployed(aid, false);

                _deployMBeanServer.unregisterAssembly(aid);
            }
        } finally {
            writeUnlockDeploy();
        }
        return result.finalResult();
    }

    public void onDeployed(DeployedAssembly assembly, boolean activate) {
        deployed(assembly, activate);

        initializeAndStart(assembly.getAssemblyId());
    }

    private void deployed(DeployedAssembly assembly, boolean activate) {
        _deployMBeanServer.registerAssembly(assembly);

        for (DeployedComponent dc : assembly.getDeployedComponents()) {
            try {
                if (LOG.isDebugEnabled())
                    LOG.debug(_("Deployed component {0}", dc));
                ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                manager.deployed(dc.getComponentId(), new File(dc.getComponentDir()), dc.getDeployedResources(),
                        activate);

                _deployMBeanServer.registerComponent(assembly, dc);
            } catch (Exception except) {
                String msg = _("Error during deployment notification of component {0}: {1}", dc.getComponentId(),
                        except);
                if (LOG.isErrorEnabled())
                    LOG.error(msg, except);
                break;
            }
        }
    }

    public void onUndeployed(DeployedAssembly assembly) {
        for (DeployedComponent dc : assembly.getDeployedComponents()) {
            try {
                if (LOG.isDebugEnabled())
                    LOG.debug(_("Undeployed component {0}", dc));
                ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                manager.undeployed(dc.getComponentId(), new File(dc.getComponentDir()), dc.getDeployedResources());
            } catch (Exception except) {
                String msg = _("Error during undeployment notification of component {0}: {1}", dc.getComponentId(),
                        except);
                if (LOG.isErrorEnabled())
                    LOG.error(msg, except);
                break;
            }
        }
    }

    public void onActivated(DeployedAssembly assembly) {
        for (DeployedComponent dc : assembly.getDeployedComponents()) {
            try {
                LOG.debug(_("Activated component {0}", dc));
                ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                manager.activated(dc.getComponentId(), new File(dc.getComponentDir()), dc.getDeployedResources());
            } catch (Exception except) {
                String msg = _("Error during activation notification of component {0}: {1}", dc.getComponentId(),
                        except);
                LOG.error(msg, except);
                break;
            }
        }
    }

    public void onRetired(DeployedAssembly assembly) {
        for (DeployedComponent dc : assembly.getDeployedComponents()) {
            try {
                LOG.debug(_("Retired component {0}", dc));
                ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                manager.retired(dc.getComponentId(), new File(dc.getComponentDir()), dc.getDeployedResources());
            } catch (Exception except) {
                String msg = _("Error during retirement notification of component {0}: {1}", dc.getComponentId(),
                        except);
                LOG.error(msg, except);
                break;
            }
        }
    }

    private void tryLock(ComponentManager manager, boolean lock) {
        if (manager instanceof ComponentManagerLockAware) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Component Manager " + manager + " is Lock Aware");
            }
            if (lock) {
                ((ComponentManagerLockAware) manager).deploymentLock();
            } else {
                ((ComponentManagerLockAware) manager).deploymentUnlock();
            }
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Component Manager " + manager
                        + " is not Lock Aware, deployment withing engine is not atomic");
            }
        }
    }

    private DeployedAssembly initializeAndStart(AssemblyId aid) {
        DeployedAssembly assembly = loadAssemblyState(aid);
        // patch with the one from DB
        DeployedAssembly assemblyFromDatabase = _persist.load().get(aid);
        if (assemblyFromDatabase != null) {
            assembly = assemblyFromDatabase;
        }
        List<DeployedAssembly> assemblies = new ArrayList<DeployedAssembly>();
        assemblies.add(assembly);
        initializeAndStart(assemblies);

        return assembly;
    }

    private boolean initializeAndStart(Collection<DeployedAssembly> assemblies) {
        boolean success = true;

        // Phase 1: Initialize all components of all assemblies
        Map<DeployedComponent, DeployedAssembly> initialized = new HashMap<DeployedComponent, DeployedAssembly>();
        for (DeployedAssembly assembly : assemblies) {
            for (DeployedComponent dc : assembly.getDeployedComponents()) {
                try {
                    LOG.debug(_("Initialize component {0}", dc));
                    ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                    manager.initialize(dc.getComponentId(), new File(dc.getComponentDir()),
                            dc.getDeployedResources(), assembly.isActive());
                    initialized.put(dc, assembly);
                } catch (Exception except) {
                    success = false;
                    String msg = _("Error during initialization of component {0}: {1}", dc.getComponentId(),
                            except);
                    LOG.error(msg, except);
                    break;
                }
            }
        }

        if (success) {
            // Phase 2: Startup all components
            for (DeployedAssembly assembly : assemblies) {
                for (DeployedComponent dc : assembly.getDeployedComponents()) {
                    try {
                        LOG.debug(_("Start component {0}", dc));
                        ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                        manager.start(dc.getComponentId(), new File(dc.getComponentDir()),
                                dc.getDeployedResources(), assembly.isActive());
                    } catch (Exception except) {
                        String msg = _("Error during startup of component {0}: {1}", dc.getComponentId(), except);
                        LOG.error(msg, except);
                    }
                }
            }
        } else {
            for (DeployedComponent dc : initialized.keySet()) {
                try {
                    ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                    manager.dispose(dc.getComponentId(), new File(dc.getComponentDir()), dc.getDeployedResources(),
                            initialized.get(dc).isActive());
                } catch (Exception except) {
                    String msg = _("Error during disposition of component {0} after startup failure: {1}",
                            dc.getComponentId(), except);
                    LOG.error(msg, except);
                }
            }
        }

        return success;
    }

    private void stopAndDispose(AssemblyId aid) {
        DeployedAssembly assembly = loadAssemblyState(aid);
        List<DeployedAssembly> assemblies = new ArrayList<DeployedAssembly>();
        assemblies.add(assembly);
        stopAndDispose(assemblies);
    }

    private void stopAndDispose(Collection<DeployedAssembly> assemblies) {
        // Phase 1: Stop all components
        for (DeployedAssembly assembly : assemblies) {
            for (DeployedComponent dc : assembly.getDeployedComponents()) {
                ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                try {
                    LOG.debug(_("Stop component {0}", dc));
                    manager.stop(dc.getComponentId(), new File(dc.getComponentDir()), dc.getDeployedResources(),
                            assembly.isActive());
                } catch (Exception except) {
                    String msg = _("Error while stopping component {0}: {1}", dc.getComponentId(), except);
                    LOG.error(msg, except);
                }
            }
        }

        // Phase 2: Dispose all components
        for (DeployedAssembly assembly : assemblies) {
            for (DeployedComponent dc : assembly.getDeployedComponents()) {
                ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                try {
                    LOG.debug(_("Dispose component {0}", dc));
                    manager.dispose(dc.getComponentId(), new File(dc.getComponentDir()), dc.getDeployedResources(),
                            assembly.isActive());
                } catch (Exception except) {
                    String msg = _("Error while disposing component {0}: {1}", dc.getComponentId(), except);
                    LOG.error(msg, except);
                }
            }
        }
    }

    public DeploymentResult activate(AssemblyId assemblyId) {
        TemporaryResult results = new TemporaryResult(assemblyId);

        DeployedAssembly assembly = loadAssemblyState(assemblyId);
        for (DeployedComponent dc : assembly.getDeployedComponents()) {
            ComponentManager manager = getComponentManager(dc.getComponentManagerName());
            try {
                LOG.debug(_("Activate component {0}", dc));
                manager.activate(dc.getComponentId(), new File(dc.getComponentDir()), dc.getDeployedResources());
            } catch (Exception except) {
                String msg = _("Error while activating component {0}: {1}", dc.getComponentId(), except);
                results.add(dc.getComponentId(), dc.getComponentManagerName(),
                        new DeploymentMessage(Level.ERROR, msg));
                LOG.error(msg, except);
            }
        }
        _persist.activate(assemblyId.getAssemblyName(), assemblyId.getAssemblyVersion());
        cluster.sendMessage(new ActivatedMessage(assembly));

        return results.finalResult();
    }

    /**
     * Retires all revisions for the assembly. The version number inside the
     * AssemblyId is ignored.
     */
    public DeploymentResult retire(AssemblyId assemblyId) {
        TemporaryResult results = new TemporaryResult(assemblyId);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Retiring components matching " + assemblyId.getAssemblyName());
        }
        Collection<DeployedAssembly> assembliesToRetire = new ArrayList<DeployedAssembly>();
        Map<AssemblyId, DeployedAssembly> assembliesById = _persist.load();
        for (AssemblyId id : assembliesById.keySet()) {
            if (id.getAssemblyName().equals(assemblyId.getAssemblyName()) && assembliesById.get(id).isActive()) {
                DeployedAssembly assembly = loadAssemblyState(id);
                assembliesToRetire.add(assembly);
                for (DeployedComponent dc : assembly.getDeployedComponents()) {
                    ComponentManager manager = getComponentManager(dc.getComponentManagerName());
                    try {
                        LOG.debug(_("Retire component {0}", dc));
                        manager.retire(dc.getComponentId(), new File(dc.getComponentDir()),
                                dc.getDeployedResources());
                    } catch (Exception except) {
                        String msg = _("Error while retiring component {0}: {1}", dc.getComponentId(), except);
                        results.add(dc.getComponentId(), dc.getComponentManagerName(),
                                new DeploymentMessage(Level.ERROR, msg));
                        LOG.error(msg, except);
                    }
                }
            }
        }
        _persist.retire(assemblyId.getAssemblyName());
        for (DeployedAssembly assembly : assembliesToRetire) {
            cluster.sendMessage(new RetiredMessage(assembly));
        }

        return results.finalResult();
    }

    private void checkRequiredComponentManagersAvailable() {
        boolean available = true;
        StringBuffer missing = new StringBuffer();
        for (String cm : _requiredComponentManagers) {
            if (!_componentManagers.containsKey(cm)) {
                if (missing.length() > 0)
                    missing.append(", ");
                missing.append(cm);
                available = false;
            }
        }
        synchronized (LIFECYCLE_LOCK) {
            if (ServiceState.STARTING.equals(_serviceState) && available) {
                synchronized (_startTask) {
                    if (!_startTask.scheduled) {
                        _startTask.scheduled = true;
                        _timer.schedule(_startTask, 0);
                    }
                }
            }
        }

        if (!available)
            LOG.info(_("Waiting for component managers: {0}", missing));
    }

    public void warmUpCluster() throws Exception {
        cluster.warmUp();
    }

    private void internalStart() {
        try {
            writeLockDeploy();
            try {
                scan();
            } catch (Exception e) {
                LOG.error(_("Error while scanning deployment repository"), e);
            }

            Collection<DeployedAssembly> assemblies = getDeployedAssemblies();
            // let the runtime ComponentManagers be aware of the deployed components
            for (DeployedAssembly assembly : assemblies) {
                for (DeployedComponent component : assembly.getDeployedComponents()) {
                    ComponentManager manager = getComponentManager(component.getComponentManagerName());
                    manager.deployed(component.getComponentId(), new File(component.getComponentDir()),
                            component.getDeployedResources(), assembly.isActive());

                    _deployMBeanServer.registerComponent(assembly, component);
                }
                _deployMBeanServer.registerAssembly(assembly);
            }

            if (initializeAndStart(assemblies)) {
                _serviceState = ServiceState.STARTED;
                LOG.info(_("DeploymentService state is now STARTED"));

                _timer.schedule(_scanTask, _scanPeriod * 1000, _scanPeriod * 1000);
                new Timer("Check Node health Timer", true).schedule(new CheckNodeHealthTask(), _scanPeriod * 1000,
                        _scanPeriod * 1000);
            }
        } finally {
            writeUnlockDeploy();
        }
    }

    private boolean isDoNotDeleteFileExists(File[] files) {
        if (files != null) {
            for (File file : files) {
                if (file.getName().equals(DO_NOT_DELETE_FILE_NAME)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Create an assembly directory
     */
    private File createAssemblyDir(AssemblyId aid) {
        File dir = getAssemblyDir(aid);
        if (dir.exists()) {
            throw new IllegalStateException("Deployment path already exists: " + dir);
        }
        LOG.debug("Creating deployment directory: " + _deployDir);
        boolean created = dir.mkdirs();
        if (!created) {
            throw new IllegalStateException("Unable to create deployment directory: " + _deployDir);
        }
        return dir;
    }

    /**
     * Create a unique assembly id, e.g. myAssembly.1, myAssembly.2, ...
     */
    private AssemblyId versionAssemblyId(String assemblyName) {
        int version = AssemblyId.NO_VERSION;
        if (new File(_deployDir, assemblyName).exists())
            version = 2;
        File[] files = Utils.listFiles(new File(_deployDir), assemblyName + ".*");
        for (File f : files) {
            String name = f.getName();
            int pos = name.lastIndexOf(".");
            try {
                int v = Integer.parseInt(name.substring(pos + 1));
                if (version == AssemblyId.NO_VERSION || v >= version)
                    version = v + 1;
            } catch (NumberFormatException e) {
                // ignore
            }
        }
        return new AssemblyId(assemblyName, version);
    }

    DeployedAssembly loadAssemblyState(AssemblyId aid) {
        File assemblyDir = getAssemblyDir(aid);
        if (!assemblyDir.exists()) {
            throw new IllegalStateException("Assembly does not exist: " + aid);
        }
        if (!assemblyDir.isDirectory()) {
            throw new IllegalArgumentException("Assembly name does not map to a directory: " + assemblyDir);
        }

        List<DeployedComponent> components = new ArrayList<DeployedComponent>();

        File[] files = assemblyDir.listFiles();

        for (File componentDir : files) {
            if (!componentDir.isDirectory()) {
                // ignore files at top-level
                continue;
            }
            int dot = componentDir.getName().lastIndexOf('.');
            if (dot < 0) {
                // ignore directories without extension (no mapping)
                continue;
            }
            String componentType = componentDir.getName().substring(dot + 1);
            String componentName = componentDir.getName().substring(0, dot);
            ComponentId component = new ComponentId(aid, componentName);
            components.add(new DeployedComponent(component, componentDir.getAbsolutePath(), componentType));
        }

        return new DeployedAssembly(aid, assemblyDir.getAbsolutePath(), components, false);
    }

    private ComponentManager getComponentManager(String componentType) {
        ComponentManager manager = _componentManagers.get(componentType);
        if (manager == null) {
            String componentManagerName = _componentTypes.get(componentType);
            if (componentManagerName != null) {
                manager = _componentManagers.get(componentManagerName);
            }
        }
        if (manager == null)
            manager = new MissingComponentManager(componentType);

        return manager;
    }

    private File getAssemblyDir(AssemblyId aid) {
        return new File(_deployDir, toDirName(aid));
    }

    private boolean exist(AssemblyId aid) {
        return getAssemblyDir(aid).exists();
    }

    private File getDeployedFile(AssemblyId aid) {
        return new File(_deployDir, toDirName(aid) + ".deployed");
    }

    private File getInvalidFile(AssemblyId aid) {
        return new File(_deployDir, toDirName(aid) + ".invalid");
    }

    private String toDirName(AssemblyId aid) {
        if (aid.getAssemblyVersion() == AssemblyId.NO_VERSION)
            return aid.getAssemblyName();
        else
            return aid.getAssemblyName() + "." + aid.getAssemblyVersion();
    }

    private boolean isMarkedAsDeployed(AssemblyId aid) {
        return getDeployedFile(aid).exists();
    }

    private void setMarkedAsDeployed(AssemblyId aid, boolean isDeployed) {
        File deployed = getDeployedFile(aid);
        if (isDeployed)
            Utils.createFile(deployed);
        else
            Utils.deleteFile(deployed);
    }

    private boolean isMarkedAsInvalid(AssemblyId aid) {
        return getInvalidFile(aid).exists();
    }

    private void clearMarkedAsInvalid(AssemblyId aid) {
        File invalid = getInvalidFile(aid);
        Utils.deleteFile(invalid);
    }

    private void setMarkedAsInvalid(AssemblyId aid, String message) {
        File invalid = getInvalidFile(aid);
        Utils.createFile(invalid);
        FileWriter writer = null;
        BufferedWriter out = null;
        try {
            writer = new FileWriter(invalid);
            out = new BufferedWriter(writer);
            out.write(message);
            out.close();
        } catch (Exception e) {
            LOG.error(_("Error while writing to {0}", invalid), e);
        } finally {
            try {
                if (writer != null)
                    writer.close();
            } catch (Exception e) {
                /* ignore */ }
        }
    }

    private DeploymentResult convertToResult(Exception except, AssemblyId aid) {
        DeploymentMessage msg = new DeploymentMessage(Level.ERROR, except.getLocalizedMessage());
        return new DeploymentResult(aid, false, msg);
    }

    static DeploymentMessage error(String message) {
        return new DeploymentMessage(Level.ERROR, message);
    }

    private DeploymentMessage error(String pattern, Object... arguments) {
        return new DeploymentMessage(Level.ERROR, _(pattern, arguments));
    }

    private DeploymentResult errorResult(AssemblyId aid, String pattern, Object... arguments) {
        DeploymentMessage msg = error(pattern, arguments);
        DeploymentResult result = new DeploymentResult(aid, false, msg);
        return result;
    }

    private AssemblyId parseAssemblyId(String dirName) {
        String assemblyName = dirName;
        int version = AssemblyId.NO_VERSION;
        int pos = dirName.length();
        while (pos > 1) {
            pos--;
            char c = dirName.charAt(pos);
            if (Character.isDigit(c))
                continue;
            if (c != '.')
                break;
            version = Integer.parseInt(dirName.substring(pos + 1));
            assemblyName = dirName.substring(0, pos);
        }
        return new AssemblyId(assemblyName, version);
    }

    private AssemblyId newAssemblyId(String assemblyName) {
        return new AssemblyId(assemblyName, AssemblyId.NO_VERSION);
    }

    private void assertStarted() {
        synchronized (LIFECYCLE_LOCK) {
            int secs = 10;
            while (secs-- > 0 && _serviceState != ServiceState.STARTED) {
                try {
                    LOG.info(
                            "Deployment has been requested. However, the service is still starting up(retrying in 1 sec).");
                    LIFECYCLE_LOCK.wait(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            if (_serviceState == ServiceState.CLUSTERIZING) {
                throw new IllegalStateException(_(
                        "Not enough number of nodes are discovered in the cluster. Deployment service will be enabled when enough nodes are available."));
            } else if (_serviceState != ServiceState.STARTED) {
                throw new IllegalStateException(_("Service not started.  Current state is {0}", _serviceState));
            }
        }
    }

    /**
     * Recurring scan of the deployment directory every "scanPeriod"
     * milliseconds
     */
    class StartTask extends TimerTask {
        boolean scheduled = false;

        public void run() {
            internalStart();
            synchronized (this) {
                scheduled = false;
            }
        }
    }

    /**
     * Recurring scan of the deployment directory every "scanPeriod"
     * milliseconds
     */
    class ScanTask extends TimerTask {
        public void run() {
            synchronized (LIFECYCLE_LOCK) {
                if (!ServiceState.STARTED.equals(_serviceState)) {
                    _timer.cancel();
                    return;
                }
            }
            try {
                scan();
            } catch (Exception e) {
                LOG.error("Error while scanning deployment repository", e);
            }
        }
    }

    private final ExecutorService executorService = Executors.newFixedThreadPool(1);

    /**
     * Recurring scan of the deployment directory every "scanPeriod"
     * milliseconds
     */
    class CheckNodeHealthTask extends TimerTask {

        public void run() {
            Callable<Boolean> checkNodeHealth = new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    if (LOG.isDebugEnabled())
                        LOG.debug("Checking the node health");

                    Boolean isNodeHealthy = Boolean.TRUE;
                    File deployDir = new File(_deployDir);
                    File[] files = deployDir.listFiles();

                    if (!deployDir.exists() || !isDoNotDeleteFileExists(files)) {
                        isNodeHealthy = Boolean.FALSE;
                    }
                    return isNodeHealthy;
                }
            };

            Future<Boolean> future = executorService.submit(checkNodeHealth);
            try {
                Boolean isNodeHealthy = future.get(2, TimeUnit.SECONDS);
                if (LOG.isDebugEnabled())
                    LOG.debug("Retrived the node health: " + isNodeHealthy.toString());
                if (isNodeHealthy) {
                    if (!NodeHealth.isNodeHealthy()) {
                        LOG.info("!!! Setting node to healthy state !!!");
                        NodeHealth.setHealthy();
                    }
                } else {
                    LOG.error(
                            "!!! FATAL ERROR !!! (Please check following error properly or Call Intalio Support for further assistance)  Deployment directory does not exists or it is not a directory: "
                                    + _deployDir);
                    if (NodeHealth.isNodeHealthy()) {
                        NodeHealth.setUnHealthy();
                    }
                }
            } catch (Exception e) {
                LOG.error(
                        "!!! FATAL ERROR !!! (Please check following error properly or Call Intalio Support for further assistance)  Deployment directory does not exists or it is not a directory: "
                                + _deployDir);
                if (NodeHealth.isNodeHealthy()) {
                    NodeHealth.setUnHealthy();
                }
            }
        }
    }

    /**
     * Callback when ComponentManager's become available/unavailable.
     * <p>
     * Note:  This implementation class needs to be public due to Java reflection limitations
     *        http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957
     */
    public class DeploymentServiceCallbackImpl implements DeploymentServiceCallback {
        public void available(ComponentManager manager) {
            String name = manager.getComponentManagerName();
            // proxy the manager for remote class loaders
            RemoteProxy<ComponentManager> proxy = new RemoteProxy<ComponentManager>(manager,
                    getClass().getClassLoader(), manager.getClass().getClassLoader());
            _componentManagers.put(name, proxy.newProxyInstance());
            LOG.info(_("ComponentManager now available: {0}", name));

            synchronized (LIFECYCLE_LOCK) {
                if (ServiceState.STARTED.equals(_serviceState)) {
                    try {
                        writeLockDeploy();

                        Collection<DeployedAssembly> assemblies = getDeployedAssemblies();
                        for (DeployedAssembly assembly : assemblies) {
                            Collection<DeployedComponent> components = assembly.getDeployedComponents();
                            for (DeployedComponent component : components) {
                                String type = _componentTypes.get(component.getComponentManagerName());
                                if (name.equals(component.getComponentManagerName()) || name.equals(type)) {
                                    try {
                                        LOG.debug(_("Initialize component {0}", component));
                                        manager.initialize(component.getComponentId(),
                                                new File(component.getComponentDir()),
                                                component.getDeployedResources(), assembly.isActive());
                                    } catch (Exception except) {
                                        LOG.error(_("Error while activating component {0}", component), except);
                                    }
                                    try {
                                        LOG.debug(_("Start component {0}", component));
                                        manager.start(component.getComponentId(),
                                                new File(component.getComponentDir()),
                                                component.getDeployedResources(), assembly.isActive());
                                    } catch (Exception except) {
                                        LOG.error(_("Error while activating component {0}", component), except);
                                    }
                                }
                            }
                        }
                    } finally {
                        writeUnlockDeploy();
                    }
                } else {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("State is " + _serviceState);
                    }
                }
            }
            checkRequiredComponentManagersAvailable();
        }

        public void unavailable(ComponentManager manager) {
            _componentManagers.remove(manager.getComponentManagerName());
        }

    }

    /**
     * Accumulate results during deployment operations
     */
    class TemporaryResult {
        private AssemblyId _aid;
        boolean _success = true;
        List<DeploymentMessage> _messages = new ArrayList<DeploymentMessage>();

        TemporaryResult(AssemblyId aid) {
            _aid = aid;
        }

        boolean add(DeployedComponent dc, DeploymentMessage msg) {
            return add(dc.getComponentId(), dc.getComponentManagerName(), msg);
        }

        boolean add(ComponentId componentId, String componentManagerName, DeploymentMessage msg) {
            _messages.add(msg);
            msg.setComponentId(componentId);
            msg.setComponentManagerName(componentManagerName);
            if (msg.isError())
                _success = false;
            return msg.isError();
        }

        boolean addAll(DeployedComponent dc, List<DeploymentMessage> messages) {
            return addAll(dc.getComponentId(), dc.getComponentManagerName(), messages);
        }

        boolean addAll(ComponentId ComponentId, String componentManagerName, List<DeploymentMessage> messages) {
            boolean localSuccess = true;
            _messages.addAll(messages);
            for (DeploymentMessage m : messages) {
                m.setComponentId(ComponentId);
                m.setComponentManagerName(componentManagerName);
                if (m.isError()) {
                    _success = false;
                    localSuccess = false;
                }
            }
            return localSuccess;
        }

        boolean isSuccessful() {
            return _success;
        }

        DeploymentResult finalResult() {
            return new DeploymentResult(_aid, _success, _messages);
        }
    }

    @ManagedAttribute
    public String getClusterType() {
        return cluster == null ? "NA" : cluster.getClass().getName();
    }

    @ManagedAttribute
    public String getServiceStatus() {
        return String.valueOf(_serviceState);
    }

    @ManagedAttribute
    public String getDiscoveredComponents() {
        return String.valueOf(_componentManagers.keySet());
    }

    @ManagedAttribute
    public String getMissingComponents() {
        Collection<String> missing = new HashSet<String>();
        missing.addAll(_requiredComponentManagers);
        missing.removeAll(_componentManagers.keySet());
        return String.valueOf(missing);
    }
}

/**
 * 
 * Possible Assembly States (0=missing, 1=exist/deployed)
 * 
 * Assembly .deployed .invalid Assembly 
 * Directory flag/file flag/file State Action(s)
 * ========== ========= ========= ========= ======================================================== 
 * 0 0 0 0 Nothing (no assembly) 
 * 0 0 0 1 Undeploy, remove from deploy.state 
 * 0 0 1 0 Nothing (no assembly) 
 * 0 0 1 1 Undeploy, remove from deploy.state 
 * 0 1 0 0 Nothing (no assembly) 
 * 0 1 0 1 Undeploy, remove from deploy.state 
 * 0 1 1 0 Nothing (no assembly) 
 * 0 1 1 1 Undeploy, remove from deploy.state 1 0 0 0 Deploy* 
 * 1 0 0 1 Undeploy, remove from deploy.state, deploy* 
 * 1 0 1 0 Nothing (invalid) 
 * 1 0 1 1 Undeploy, remove from deploy.state 
 * 1 1 0 0 Nothing (ignore => conservative to avoid undeploy) 
 * 1 1 0 1 Nothing (normal) 
 * 1 1 1 0 Nothing (invalid) 
 * 1 1 1 1 Nothing (already deployed?)
 * 
 * where deploy* = create .invalid, deploy, if successful, create .deployed and remove .invalid
 * 
 * In Java logic,
 * 
 * if (!assemblyDir && deploy.state) undeploy, remove from deploy.state 
 * if (assemblyDir && !.deployed && deploy.state) undeploy, remove from deploy.state 
 * if (assemblyDir && !.deployed && !.invalid && !deploy.state) deploy
 * 
 */