/**
 * Copyright (c) 2007-2012 EBM WebSourcing, 2012-2015 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see <http://www.gnu.org/licenses/>
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.microkernel.configuration;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.objectweb.fractal.fraclet.annotation.annotations.FractalComponent;
import org.objectweb.fractal.fraclet.annotation.annotations.Interface;
import org.objectweb.fractal.fraclet.annotation.annotations.LifeCycle;
import org.objectweb.fractal.fraclet.annotation.annotations.Provides;
import org.objectweb.fractal.fraclet.annotation.annotations.type.LifeCycleType;
import org.ow2.petals.clientserverapi.configuration.ContainerConfiguration;
import org.ow2.petals.clientserverapi.configuration.ContainerConfiguration.ContainerState;
import org.ow2.petals.launcher.api.server.conf.ConfigurationProperties;
import org.ow2.petals.microkernel.api.configuration.ConfigurationService;
import org.ow2.petals.microkernel.api.configuration.DomainConfiguration;
import org.ow2.petals.microkernel.api.configuration.JndiConfiguration;
import org.ow2.petals.microkernel.api.configuration.SubDomainConfiguration;
import org.ow2.petals.microkernel.api.configuration.exception.ConfigurationException;
import org.ow2.petals.microkernel.api.configuration.exception.ContainerUnknownException;
import org.ow2.petals.microkernel.api.configuration.exception.InconsistentConfigurationException;
import org.ow2.petals.microkernel.api.configuration.exception.InconsistentDomainModeConfigurationException;
import org.ow2.petals.microkernel.api.configuration.exception.InconsistentNetworkTimeSyncException;
import org.ow2.petals.microkernel.api.configuration.exception.SubdomainUnknownException;
import org.ow2.petals.microkernel.api.jbi.messaging.RouterService;
import org.ow2.petals.microkernel.api.system.persistence.PersistenceService;
import org.ow2.petals.microkernel.api.util.LoggingUtil;
import org.ow2.petals.topology.TopologyHelper;
import org.ow2.petals.topology.generated.Container;
import org.ow2.petals.topology.generated.Domain;
import org.ow2.petals.topology.generated.DomainMode;
import org.ow2.petals.topology.generated.JmxService;
import org.ow2.petals.topology.generated.Jndi;
import org.ow2.petals.topology.generated.NodeType;
import org.ow2.petals.topology.generated.RegistryService;
import org.ow2.petals.topology.generated.Subdomain;
import org.ow2.petals.topology.generated.SubdomainMode;
import org.ow2.petals.topology.generated.Topology;
import org.ow2.petals.topology.generated.TransportService;
import org.ow2.petals.topology.generated.WebServiceService;

import com.ebmwebsourcing.easycommons.lang.StringHelper;
import com.ebmwebsourcing.easycommons.properties.PropertiesException;
import com.ebmwebsourcing.easycommons.properties.PropertiesHelper;

/**
 * <p>
 * The Configuration service that handles the configuration of the local PEtALS
 * container.
 * </p>
 * <p>
 * At its starting, it retrieves the configuration from the {@link Topology} and
 * the local container configuration (ie, content of the file
 * 'server.properties'), it performs a first level validation of the
 * consistency, then it fill the local {@link ContainerConfiguration}.
 * </p>
 * 
 * @author Roland NAUDIN - EBM WebSourcing
 * @author Christophe Hamerling - EBM WebSourcing
 * @author Charles Casadei - EBM WebSourcing
 * @since Petals 2.0
 */
@FractalComponent
@Provides(interfaces = @Interface(name = "service", signature = ConfigurationService.class))
public class ConfigurationServiceImpl implements ConfigurationService {

    private class SubDomainAndContainerNames {

        protected String containerName;

        protected String subDomainName;
    }

    /**
     * Logging utilities
     */
    protected LoggingUtil log;

    // Local informations
    private final ContainerConfiguration containerConfiguration = new ContainerConfiguration();

    /**
     * This is the initial containers configuration created from the topology
     * file. Key is the container name.
     */
    private final Map<String, ContainerConfiguration> containersConfig = new HashMap<String, ContainerConfiguration>();

    private final DomainConfiguration domainConfiguration = new DomainConfiguration();



    private final SubDomainConfiguration subDomainConfiguration = new SubDomainConfiguration();

    // global informations

    /**
     * The subdomains configuration created from the topology file. Key is the
     * subdomain name.
     */
    private final Map<String, SubDomainConfiguration> subDomainsConfig = new HashMap<String, SubDomainConfiguration>();

    private Topology topology;

    /**
     * {@inheritDoc}
     */
    public synchronized void addContainerConfiguration(ContainerConfiguration addedContainer)
            throws ConfigurationException {

        if (addedContainer == null) {
            StringBuilder errorSB = new StringBuilder();
            errorSB.append("Can't add a null container to the local topology");
            throw new ConfigurationException(errorSB.toString());
        }

        final String localContainerName = this.containerConfiguration.getName();

        // Retrieve the container name
        String addedContainerName = null;
        if (addedContainer.getName() == null) {
            StringBuilder errorSB = new StringBuilder();
            errorSB.append("Can't retrieve the container name");
            throw new ConfigurationException(errorSB.toString());
        }
        addedContainerName = addedContainer.getName();

        if (addedContainerName.equals(localContainerName)) {
            StringBuilder errorSB = new StringBuilder();
            errorSB.append("Can't add a container with the same name as the current one");
            throw new ConfigurationException(errorSB.toString());
        }

        String addedContainerSubdomainName = addedContainer.getSubdomainName();
        if (addedContainerSubdomainName == null) {
            StringBuilder errorSB = new StringBuilder();
            errorSB.append("Null subdomain found for the added container : " + addedContainer);
            throw new ConfigurationException(errorSB.toString());
        }

        if (this.containersConfig.containsKey(addedContainer.getName())) {
            this.removeContainerConfiguration(addedContainer);
        }

        // Adds the container to the "JAXB Topology" file
        List<Subdomain> jaxbSubdomains = this.topology.getDomain().getSubDomain();
        Subdomain jaxbSubdomainOfTheAddedContainer = null;
        for (Subdomain jaxbSubdomain : jaxbSubdomains) {
            if (jaxbSubdomain.getName().equals(addedContainerSubdomainName)) {
                jaxbSubdomainOfTheAddedContainer = jaxbSubdomain;
                break;
            }
        }

        if (jaxbSubdomainOfTheAddedContainer == null) {
            StringBuilder errorSB = new StringBuilder();
            errorSB.append("No subdomain found for the added container : " + addedContainer);
            throw new ConfigurationException(errorSB.toString());
        }

        Container addedJaxbContainer = new Container();
        addedJaxbContainer.setName(addedContainer.getName());
        addedJaxbContainer.setDescription(addedContainer.getDescription());
        addedJaxbContainer.setHost(addedContainer.getHost());
        addedJaxbContainer.setUser(addedContainer.getUser());
        addedJaxbContainer.setPassword(addedContainer.getPassword());

        JmxService jaxbJmxService = new JmxService();
        jaxbJmxService.setRmiPort(addedContainer.getJmxRMIConnectorPort());
        addedJaxbContainer.setJmxService(jaxbJmxService);

        WebServiceService jaxbWebService = new WebServiceService();
        jaxbWebService.setPort(addedContainer.getWebservicePort());
        jaxbWebService.setPrefix(addedContainer.getWebservicePrefix());
        addedJaxbContainer.setWebserviceService(jaxbWebService);

        RegistryService jaxbRegistryService = new RegistryService();
        jaxbRegistryService.setPort(addedContainer.getRegistryPort());
        addedJaxbContainer.setRegistryService(jaxbRegistryService);

        TransportService jaxbTransportService = new TransportService();
        jaxbTransportService.setTcpPort(addedContainer.getTCPPort());
        addedJaxbContainer.setTransportService(jaxbTransportService);

        switch (addedContainer.getRegistryMode()) {
            case PEER:
                addedJaxbContainer.setType(NodeType.PEER);
                break;
            case MASTER:
                addedJaxbContainer.setType(NodeType.MASTER);
                break;
            case SLAVE:
                addedJaxbContainer.setType(NodeType.SLAVE);
                break;
            case STANDALONE:
                addedJaxbContainer.setType(NodeType.STANDALONE);
                break;
            default:
                throw new ConfigurationException("Unknown registry mode");
        }

        jaxbSubdomainOfTheAddedContainer.getContainer().add(addedJaxbContainer);

        // Store the new container to the topology stored in memory
        this.containersConfig.put(addedContainerName, addedContainer);
    }

    /**
     * {@inheritDoc}
     */
    public synchronized void addSubdomainConfiguration(SubDomainConfiguration addedSubdomain)
            throws ConfigurationException {
        if (addedSubdomain == null) {
            throw new ConfigurationException("Can't add a null subdomain to the local topology");
        }

        if (this.subDomainsConfig.containsKey(addedSubdomain.getName())) {
            throw new ConfigurationException("Subdomain already registered for name : "
                    + addedSubdomain.getName());
        } else {
            this.subDomainsConfig.put(addedSubdomain.getName(), addedSubdomain);

            // Add the new subdomain to the JAXB topology holder
            Subdomain jaxbSubdomain = new Subdomain();

            jaxbSubdomain.setName(addedSubdomain.getName());
            jaxbSubdomain.setDescription(addedSubdomain.getDescription());
            jaxbSubdomain.setMode(addedSubdomain.getMode());
            jaxbSubdomain.setNetworkTimeSynchronized(addedSubdomain.isNetworkTimeSynchronized());

            this.topology.getDomain().getSubDomain().add(jaxbSubdomain);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @seeorg.ow2.petals.kernel.configuration.ConfigurationService#
     * getContainerConfiguration()
     */
    public ContainerConfiguration getContainerConfiguration() {
        return this.containerConfiguration;
    }

    /**
     * {@inheritDoc}
     */
    public Map<String, String> getAllServerProperties() {
        final Map<String, String> containerLocalProperties = new HashMap<String, String>();

        containerLocalProperties.put(ConfigurationProperties.DATA_ROOT_DIRECTORY_PROPERTY_NAME,
                this.containerConfiguration.getDataRootPath());
        containerLocalProperties.put(ConfigurationProperties.REPOSITORY_PATH_PROPERTY_NAME,
                this.containerConfiguration.getRepositoryDirectoryPath());
        containerLocalProperties.put(ConfigurationProperties.WORKING_AREA_PATH_PROPERTY_NAME,
                this.containerConfiguration.getWorkDirectoryPath());
        containerLocalProperties.put(ConfigurationProperties.EXCHANGE_VALIDATION,
                Boolean.toString(this.containerConfiguration.isExchangeValidation()));
        containerLocalProperties.put(ConfigurationProperties.TRANSPORT_QUEUE_MAXSIZE,
                Integer.toString(this.containerConfiguration.getTransporterQueueMaxSize()));
        containerLocalProperties.put(ConfigurationProperties.TRANSPORT_QUEUE_OFFERING_TIMEOUT,
                Long.toString(this.containerConfiguration.getTransporterQueueOfferingTimeout()));
        containerLocalProperties.put(ConfigurationProperties.TRANSPORT_TCP_RECEIVERS,
                Integer.toString(this.containerConfiguration.getTCPReceivers()));
        containerLocalProperties.put(ConfigurationProperties.TRANSPORT_TCP_SENDERS,
                Integer.toString(this.containerConfiguration.getTCPSenders()));
        containerLocalProperties.put(ConfigurationProperties.TRANSPORT_TCP_CONNECTION_TIMEOUT,
                Long.toString(this.containerConfiguration.getTCPConnectionTimeout()));
        containerLocalProperties.put(ConfigurationProperties.TRANSPORT_TCP_SEND_TIMEOUT,
                Long.toString(this.containerConfiguration.getTCPSendTimeout()));
        containerLocalProperties.put(ConfigurationProperties.TRANSPORT_TCP_SEND_EVICTOR_DELAY,
                Long.toString(this.containerConfiguration.getTCPSenderEvictorDelay()));
        containerLocalProperties.put(ConfigurationProperties.TRANSPORT_TCP_SEND_EVICTABLE_DELAY,
                Long.toString(this.containerConfiguration.getTCPSenderEvictableDelay()));
        containerLocalProperties.put(ConfigurationProperties.ROUTER_QOS,
                this.containerConfiguration.getRouterQOS());
        containerLocalProperties.put(ConfigurationProperties.ROUTER_STRATEGY,
                this.containerConfiguration.getRouterStrategy());
        containerLocalProperties.put(ConfigurationProperties.ROUTER_SEND_ATTEMPT,
                Short.toString(this.containerConfiguration.getRouterSendAttempt()));
        containerLocalProperties.put(ConfigurationProperties.ROUTER_SEND_DELAY,
                Integer.toString(this.containerConfiguration.getRouterSendDelay()));
        containerLocalProperties.put(ConfigurationProperties.TASK_TIMEOUT,
                Long.toString(this.containerConfiguration.getTaskTimeout()));
        containerLocalProperties.put(ConfigurationProperties.CLASSLOADER_ISOLATED,
                Boolean.toString(this.containerConfiguration.isIsolateJBIClassLoaders()));
        containerLocalProperties.put(ConfigurationProperties.AUTOLOADER,
                Boolean.toString(this.containerConfiguration.isActivateAutoloader()));
        containerLocalProperties.put(ConfigurationProperties.SSL_KEY_PASSWORD,
                this.containerConfiguration.getSSLKeyPassword());
        containerLocalProperties.put(ConfigurationProperties.SSL_KEYSTORE_FILE,
                this.containerConfiguration.getSSLKeystore());
        containerLocalProperties.put(ConfigurationProperties.SSL_KEYSTORE_PASSWORD,
                this.containerConfiguration.getSSLKeystorePassword());
        containerLocalProperties.put(ConfigurationProperties.SSL_TRUSTSTORE_FILE,
                this.containerConfiguration.getSSLTruststore());
        containerLocalProperties.put(ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD,
                this.containerConfiguration.getSSLTruststorePassword());
        containerLocalProperties.put(ConfigurationProperties.REGISTRY_TRANSPORT_TIMEOUT,
                Integer.toString(this.containerConfiguration.getRegistryTransportTimeout()));
        containerLocalProperties.put(ConfigurationProperties.REGISTRY_SYNCHRO_PERIOD,
                Integer.toString(this.containerConfiguration.getRegistrySynchroPeriod()));
        containerLocalProperties.put(ConfigurationProperties.REGISTRY_REPOSITORY,
                this.containerConfiguration.getRegistryRepository());
        containerLocalProperties.put(ConfigurationProperties.REGISTRY_DB_DRIVER,
                this.containerConfiguration.getRegistryDatabaseDriver());
        containerLocalProperties.put(ConfigurationProperties.REGISTRY_DB_URL,
                this.containerConfiguration.getRegistryDatabaseUrl());
        containerLocalProperties.put(ConfigurationProperties.REGISTRY_DB_USER,
                this.containerConfiguration.getRegistryDatabaseUser());
        containerLocalProperties.put(ConfigurationProperties.REGISTRY_DB_PASSWORD,
                this.containerConfiguration.getRegistryDatabasePassword());
        containerLocalProperties.put(ConfigurationProperties.REGISTRY_DB_DIALECT,
                this.containerConfiguration.getRegistryDatabaseDialect());
        containerLocalProperties.put(ConfigurationProperties.PERSISTENCE_DURATION,
                Long.toString(this.containerConfiguration.getPersistenceDuration()));
        containerLocalProperties.put(ConfigurationProperties.PERSISTENCE_FETCHSIZE,
                Integer.toString(this.containerConfiguration.getPersistenceFetchSize()));
        containerLocalProperties.put(ConfigurationProperties.RECOVERY_CORE_POOL_SIZE,
                Integer.toString(this.containerConfiguration.getRecoveryCorePoolSize()));
        containerLocalProperties.put(ConfigurationProperties.RECOVERY_MAX_POOL_SIZE,
                Integer.toString(this.containerConfiguration.getRecoveryMaxPoolSize()));
        containerLocalProperties.put(ConfigurationProperties.RECOVERY_KEEP_ALIVE_TIME,
                Long.toString(this.containerConfiguration.getRecoveryKeepAliveTime()));

        // Add user properties
        final Map<String, String> userConfiguration = this.containerConfiguration
                .getUserConfiguration();
        if (userConfiguration != null) {
            containerLocalProperties.putAll(userConfiguration);
        }

        // Add extra properties
        final Map<String, String> extraConfiguration = this.containerConfiguration
                .getExtraConfiguration();
        if (extraConfiguration != null) {
            containerLocalProperties.putAll(extraConfiguration);
        }

        return containerLocalProperties;
    }
    
    /**
     * {@inheritDoc}
     */
    public Properties getServerProperties() {
        final Properties serverProperties = new Properties();
        for (final Map.Entry<String, String> entry : this.getAllServerProperties().entrySet()) {
            final String value = entry.getValue();
            if (value != null) {
                serverProperties.setProperty(entry.getKey(), value);
            }
        }
        return serverProperties;
    }

    /**
     * {@inheritDoc}
     */
    public synchronized ContainerConfiguration getContainerConfiguration(String containerName) {
        ContainerConfiguration result = null;
        if (this.containerConfiguration.getName().equals(containerName)) {
            result = this.getContainerConfiguration();
        } else {
            result = this.containersConfig.get(containerName);
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public synchronized Set<ContainerConfiguration> getContainersConfiguration() {
        Set<ContainerConfiguration> result = new HashSet<ContainerConfiguration>(
                this.containersConfig.size());
        for (ContainerConfiguration containerCfg : this.containersConfig.values()) {
            result.add(containerCfg);
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public synchronized DomainConfiguration getDomainConfiguration() {
        this.log.call();
        return this.domainConfiguration;
    }

    /**
     * {@inheritDoc}
     */
    public synchronized SubDomainConfiguration getSubDomainConfiguration() {
        this.log.call();
        return this.subDomainConfiguration;
    }

    /**
     * {@inheritDoc}
     */
    public synchronized Set<SubDomainConfiguration> getSubDomainsConfiguration() {
        Set<SubDomainConfiguration> result = new HashSet<SubDomainConfiguration>(
                this.subDomainsConfig.size());
        for (SubDomainConfiguration subDomainCfg : this.subDomainsConfig.values()) {
            result.add(subDomainCfg);
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public synchronized Topology getTopology() throws ConfigurationException {
        this.log.call();

        return this.topology;
    }

    public synchronized void removeContainerConfiguration(ContainerConfiguration removedContainer)
            throws ConfigurationException {
        if (removedContainer == null) {
            throw new ConfigurationException("Can't remove container : null");
        }

        if (!this.containersConfig.containsKey(removedContainer.getName())) {
            throw new ConfigurationException("No container found for name : "
                    + removedContainer.getName());
        }

        String removedContainerSubdomainName = removedContainer.getSubdomainName();
        if (removedContainerSubdomainName == null) {
            throw new ConfigurationException("No subdomain name found for the removed container");
        }

        List<Subdomain> jaxbSubdomains = this.topology.getDomain().getSubDomain();
        Subdomain jaxbSubdomainOfTheRemovedContainer = null;

        for (Subdomain jaxbSubdomain : jaxbSubdomains) {
            if (jaxbSubdomain.getName().equals(removedContainerSubdomainName)) {
                jaxbSubdomainOfTheRemovedContainer = jaxbSubdomain;
                break;
            }
        }

        if (jaxbSubdomainOfTheRemovedContainer == null) {
            throw new ConfigurationException("No subdomain found for the added container : "
                    + removedContainer);
        }

        for (Container jaxbContainer : jaxbSubdomainOfTheRemovedContainer.getContainer()) {
            if (jaxbContainer.getName().equals(removedContainer.getName())) {
                jaxbSubdomainOfTheRemovedContainer.getContainer().remove(jaxbContainer);
                break;
            }
        }

        this.containersConfig.remove(removedContainer.getName());
    }

    public synchronized void removeSubdomainConfiguration(String removedSubdomainName)
            throws ConfigurationException {
        if (removedSubdomainName == null) {
            throw new ConfigurationException("Can't remove subdomain : null");
        }

        if (!this.subDomainsConfig.containsKey(removedSubdomainName)) {
            throw new ConfigurationException("No subdomain found for name : "
                    + removedSubdomainName);
        }

        Subdomain removedJaxbSubdomain = null;
        for (Subdomain jaxbSubdomain : this.topology.getDomain().getSubDomain()) {
            if (jaxbSubdomain.getName().equals(removedSubdomainName)) {
                this.topology.getDomain().getSubDomain().remove(jaxbSubdomain);
                removedJaxbSubdomain = jaxbSubdomain;
                break;
            }
        }

        if (removedJaxbSubdomain == null) {
            throw new ConfigurationException("No subdomain found for name : "
                    + removedSubdomainName);
        }

        this.subDomainsConfig.remove(removedSubdomainName);
        this.removeSubdomainContainers(removedSubdomainName);
    }

    /**
     * {@inheritDoc}
     */
    public synchronized void setContainerState(ContainerState state) {
        this.containerConfiguration.setState(state);
    }

    /**
     * {@inheritDoc}
     */
    public void validateDynamicTopology() throws ConfigurationException {

        this.verifyDomain(this.domainConfiguration);

        for (Map.Entry<String, SubDomainConfiguration> subdomainEntry : this.subDomainsConfig
                .entrySet()) {
            this.verifySubDomain(subdomainEntry.getValue());
        }

        for (Map.Entry<String, ContainerConfiguration> containerEntry : this.containersConfig
                .entrySet()) {
            this.verifyContainer(containerEntry.getValue());
        }
    }

    /**
     * {@inheritDoc}
     */
    public synchronized void verifyContainerTopology(
            final ContainerConfiguration newContainerConfiguration,
            final SubDomainConfiguration newSubDomainConfiguration,
            final DomainConfiguration newDomainConfiguration)
            throws InconsistentConfigurationException {
        this.log.start();

        this.verifyDomain(newDomainConfiguration);

        this.verifySubDomain(newSubDomainConfiguration);

        this.verifyContainer(newContainerConfiguration);

        this.log.end();
    }

    /**
     * @see org.objectweb.fractal.api.control.LifeCycleController#startFc()
     */
    @LifeCycle(on = LifeCycleType.START)
    protected void start() throws Exception {

        this.log = new LoggingUtil(
                java.util.logging.Logger.getLogger(ConfigurationService.COMPONENT_LOGGER_NAME));
        this.log.start();

        this.log.end();
    }

    @LifeCycle(on = LifeCycleType.STOP)
    protected void stop() throws Exception {
        this.log.call();
    }

    /**
     * {@inheritDoc}
     */
    public void loadConfiguration(final Properties serverLocalProperties, final Topology topology)
            throws ConfigurationException {

        try {

            this.topology = topology;

            this.fillLocalProperties(serverLocalProperties);

            String containerName = this.containerConfiguration.getName();
            if (containerName == null) {
                containerName = System.getProperty(ConfigurationProperties.CONTAINER_NAME);
            }
            final SubDomainAndContainerNames values = this.validateTopology(containerName);

            this.fillGlobalTopology();

            this.fillSubDomainTopology(values.subDomainName);

            this.fillLocalTopology(values.containerName);

            this.fillGlobalContainerConfig();

            this.fillGlobalDomainConfig();

            // post validate platform configuration
            this.postValidate();

            if (this.log.isDebugEnabled()) {
                this.log.debug("Configuration used :");
                this.log.debug(this.containerConfiguration);
            }

            this.containersConfig.put(this.containerConfiguration.getName(),
                    this.containerConfiguration);
        } catch (final ConfigurationException e) {
            this.log.error("Failed to load configuration", e);
            throw e;
        }
    }

    private void fillGlobalContainerConfig() {
        Collection<Container> containers = TopologyHelper.findAllContainers(this.topology);
        for (Container container : containers) {
            ContainerConfiguration cc = new ContainerConfiguration();
            ContainerConfigurationHelper.fill(cc, container);
            cc.setSubdomainName(TopologyHelper.findSubdomain(container, this.topology).getName());
            this.containersConfig.put(cc.getName(), cc);
        }
    }

    private void fillGlobalDomainConfig() {
        List<Subdomain> list = this.topology.getDomain().getSubDomain();
        for (Subdomain subdomain : list) {
            SubDomainConfiguration subDomainCfg = new SubDomainConfiguration();
            subDomainCfg.setDescription(subdomain.getDescription());
            subDomainCfg.setName(subdomain.getName());
            subDomainCfg.setMode(subdomain.getMode());
            this.subDomainsConfig.put(subDomainCfg.getName(), subDomainCfg);
        }
    }

    /**
     * Fill the global topology.
     * 
     * @throws ConfigurationException
     */
    private void fillGlobalTopology() throws ConfigurationException {
        this.log.start();

        // domain mode
        this.domainConfiguration.setName(this.topology.getDomain().getName());
        this.domainConfiguration.setDescription(this.topology.getDomain().getDescription());
        if (DomainMode.STATIC.equals(this.topology.getDomain().getMode())) {
            this.domainConfiguration
                    .setMode(org.ow2.petals.microkernel.api.configuration.DomainConfiguration.DomainMode.STATIC);
        } else if (DomainMode.DYNAMIC.equals(this.topology.getDomain().getMode())) {
            this.domainConfiguration
                    .setMode(org.ow2.petals.microkernel.api.configuration.DomainConfiguration.DomainMode.DYNAMIC);
        } else {
            throw new ConfigurationException("Unknown domain mode");
        }

        // domain JNDI
        if (this.topology.getDomain().getJndi() != null) {
            JndiConfiguration jndiConfiguration = new JndiConfiguration();

            jndiConfiguration.setJndiFactory(this.topology.getDomain().getJndi().getFactory());
            try {
                jndiConfiguration.setJndiProviderUrl(new URI(this.topology.getDomain().getJndi()
                        .getProviderUrl()));
            } catch (URISyntaxException e) {
                throw new ConfigurationException(e);
            }
            jndiConfiguration.setJndiSecurityPrincipal(this.topology.getDomain().getJndi()
                    .getSecurityPrincipal());
            jndiConfiguration.setJndiSecurityCredentials(this.topology.getDomain().getJndi()
                    .getSecurityCredentials());

            jndiConfiguration.setJndiPoolSize(this.topology.getDomain().getJndi().getPoolSize());
            jndiConfiguration.setJndiBatchSize(this.topology.getDomain().getJndi().getBatchSize());

            this.domainConfiguration.setJndiConfiguration(jndiConfiguration);
        }

        this.log.end();
    }

    /**
     * Initialize the container configuration with the current server
     * properties.
     * 
     * @param serverLocalProperties
     *            The local properties of the current container (ie: the content
     *            of 'server.properties' or equivalent). Can be
     *            <code>null</code>.
     * @throws ConfigurationException
     *             An error occurs about a property value
     */
    private void fillLocalProperties(final Properties serverLocalProperties)
            throws ConfigurationException {
        this.log.start();

        final Map<String, String> userProperties = new HashMap<String, String>();

        final Map<String, String> extraProperties = new HashMap<String, String>();

        if (serverLocalProperties != null) {
            for (final Map.Entry<Object, Object> entry : serverLocalProperties.entrySet()) {
                final Object keyObj = entry.getKey();
                final String value = (String) entry.getValue();
                if (keyObj instanceof String) {
                    final String key = (String) keyObj;
                    if (key.equals(ConfigurationProperties.CONTAINER_NAME)) {
                        this.containerConfiguration.setName(value);
                    } else if (key
                            .equals(ConfigurationProperties.DATA_ROOT_DIRECTORY_PROPERTY_NAME)) {
                        this.containerConfiguration.setDataRootPath(value);
                    } else if (key.equals(ConfigurationProperties.REPOSITORY_PATH_PROPERTY_NAME)) {
                        this.containerConfiguration.setRepositoryDirectoryPath(value);
                    } else if (key.equals(ConfigurationProperties.WORKING_AREA_PATH_PROPERTY_NAME)) {
                        this.containerConfiguration.setWorkDirectoryPath(value);
                    } else if (key.equals(ConfigurationProperties.EXCHANGE_VALIDATION)) {
                        this.containerConfiguration.setExchangeValidation(Boolean
                                .parseBoolean(value));
                    } else if (key.equals(ConfigurationProperties.TRANSPORT_QUEUE_MAXSIZE)) {
                        this.containerConfiguration.setTransporterQueueMaxSize(Integer
                                .parseInt(value));
                    } else if (key.equals(ConfigurationProperties.TRANSPORT_QUEUE_OFFERING_TIMEOUT)) {
                        this.containerConfiguration.setTransporterQueueOfferingTimeout(Long
                                .parseLong(value));
                    } else if (key.equals(ConfigurationProperties.TRANSPORT_TCP_RECEIVERS)) {
                        this.containerConfiguration.setTCPReceivers(Integer.parseInt(value));
                    } else if (key.equals(ConfigurationProperties.TRANSPORT_TCP_SENDERS)) {
                        this.containerConfiguration.setTCPSenders(Integer.parseInt(value));
                    } else if (key.equals(ConfigurationProperties.TRANSPORT_TCP_CONNECTION_TIMEOUT)) {
                        this.containerConfiguration.setTCPConnectionTimeout(Long.parseLong(value));
                    } else if (key.equals(ConfigurationProperties.TRANSPORT_TCP_SEND_TIMEOUT)) {
                        this.containerConfiguration.setTCPSendTimeout(Long.parseLong(value));
                    } else if (key.equals(ConfigurationProperties.TRANSPORT_TCP_SEND_EVICTOR_DELAY)) {
                        this.containerConfiguration.setTCPSenderEvictorDelay(Long.parseLong(value));
                    } else if (key
                            .equals(ConfigurationProperties.TRANSPORT_TCP_SEND_EVICTABLE_DELAY)) {
                        this.containerConfiguration.setTCPSenderEvictableDelay(Long
                                .parseLong(value));
                    } else if (key.equals(ConfigurationProperties.ROUTER_QOS)) {
                        this.containerConfiguration.setRouterQOS(value);
                    } else if (key.equals(ConfigurationProperties.ROUTER_STRATEGY)) {
                        this.containerConfiguration.setRouterStrategy(value);
                    } else if (key.equals(ConfigurationProperties.ROUTER_SEND_ATTEMPT)) {
                        this.containerConfiguration.setRouterSendAttempt(Short.parseShort(value));
                    } else if (key.equals(ConfigurationProperties.ROUTER_SEND_DELAY)) {
                        this.containerConfiguration.setRouterSendDelay(Integer.parseInt(value));
                    } else if (key.equals(ConfigurationProperties.TASK_TIMEOUT)) {
                        this.containerConfiguration.setTaskTimeout(Long.parseLong(value));
                    } else if (key.equals(ConfigurationProperties.CLASSLOADER_ISOLATED)) {
                        this.containerConfiguration.setIsolateJBIClassLoaders(Boolean
                                .parseBoolean(value));
                    } else if (key.equals(ConfigurationProperties.AUTOLOADER)) {
                        this.containerConfiguration.setActivateAutoloader(Boolean
                                .parseBoolean(value));
                    } else if (key.equals(ConfigurationProperties.SSL_KEY_PASSWORD)) {
                        this.containerConfiguration.setSSLKeyPassword(value);
                    } else if (key.equals(ConfigurationProperties.SSL_KEYSTORE_FILE)) {
                        this.containerConfiguration.setSSLKeystore(value);
                    } else if (key.equals(ConfigurationProperties.SSL_KEYSTORE_PASSWORD)) {
                        this.containerConfiguration.setSSLKeystorePassword(value);
                    } else if (key.equals(ConfigurationProperties.SSL_TRUSTSTORE_FILE)) {
                        this.containerConfiguration.setSSLTruststore(value);
                    } else if (key.equals(ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD)) {
                        this.containerConfiguration.setSSLTruststorePassword(value);
                    } else if (key.equals(ConfigurationProperties.REGISTRY_TRANSPORT_TIMEOUT)) {
                        this.containerConfiguration.setRegistryTransportTimeout(Integer
                                .parseInt(value));
                    } else if (key.equals(ConfigurationProperties.REGISTRY_SYNCHRO_PERIOD)) {
                        this.containerConfiguration.setRegistrySynchroPeriod(Integer
                                .parseInt(value));
                    } else if (key.equals(ConfigurationProperties.REGISTRY_REPOSITORY)) {
                        this.containerConfiguration.setRegistryRepository(value);
                    } else if (key.equals(ConfigurationProperties.REGISTRY_DB_DRIVER)) {
                        this.containerConfiguration.setRegistryDatabaseDriver(value);
                    } else if (key.equals(ConfigurationProperties.REGISTRY_DB_URL)) {
                        this.containerConfiguration.setRegistryDatabaseUrl(value);
                    } else if (key.equals(ConfigurationProperties.REGISTRY_DB_USER)) {
                        this.containerConfiguration.setRegistryDatabaseUser(value);
                    } else if (key.equals(ConfigurationProperties.REGISTRY_DB_PASSWORD)) {
                        this.containerConfiguration.setRegistryDatabasePassword(value);
                    } else if (key.equals(ConfigurationProperties.REGISTRY_DB_DIALECT)) {
                        this.containerConfiguration.setRegistryDatabaseDialect(value);
                    } else if (key.equals(ConfigurationProperties.PERSISTENCE_DURATION)) {
                        this.containerConfiguration.setPersistenceDuration(Long.parseLong(value));
                    } else if (key.equals(ConfigurationProperties.PERSISTENCE_FETCHSIZE)) {
                        this.containerConfiguration
                                .setPersistenceFetchSize(Integer.parseInt(value));
                    } else if (key.equals(ConfigurationProperties.TOPOLOGY_PASSPHRASE)) {
                        this.containerConfiguration.setTopologyPassPhrase(serverLocalProperties
                                .getProperty(key, null));
                    } else if (key.startsWith(ConfigurationProperties.USER_CONFIG_PREFIX + ".")) {
                        userProperties.put(key, value);
                    } else if (key.equals(ConfigurationProperties.RECOVERY_CORE_POOL_SIZE)) {
                        this.containerConfiguration
                                .setRecoveryCorePoolSize(Integer.parseInt(value));
                    } else if (key.equals(ConfigurationProperties.RECOVERY_MAX_POOL_SIZE)) {
                        this.containerConfiguration.setRecoveryMaxPoolSize(Integer.parseInt(value));
                    } else if (key.equals(ConfigurationProperties.RECOVERY_KEEP_ALIVE_TIME)) {
                        this.containerConfiguration.setRecoveryKeepAliveTime(Long.parseLong(value));
                    } else {
                        extraProperties.put(key, value);
                    }
                }
            }
        }

        if (userProperties.size() > 0) {
            this.containerConfiguration.setUserConfiguration(userProperties);
        }

        this.containerConfiguration.setExtraConfiguration(extraProperties);

        // Set default value to not set required properties
        try {
            if (this.containerConfiguration.getRepositoryDirectoryPath() == null) {
                this.containerConfiguration.setRepositoryDirectoryPath(PropertiesHelper
                        .resolveString(ConfigurationProperties.DEFAULT_REPOSITORY_PATH,
                                serverLocalProperties));
            }

            if (this.containerConfiguration.getWorkDirectoryPath() == null) {
                this.containerConfiguration.setWorkDirectoryPath(PropertiesHelper.resolveString(
                        ConfigurationProperties.DEFAULT_WORKING_AREA_PATH, serverLocalProperties));
            }

            // TODO: Should be move in the right fractal component statup when
            // Persistence service and Registry service will use different
            // parameters
            if (this.containerConfiguration.getRegistryDatabaseUrl() == null) {
                this.containerConfiguration
                        .setRegistryDatabaseUrl(PersistenceService.DEFAULT_URL_PREFIX
                                + this.containerConfiguration.getWorkDirectoryPath()
                                + File.separator + PersistenceService.DEFAULT_URL_SUFFIX);
            }

        } catch (final PropertiesException e) {
            throw new ConfigurationException(e);
        }

        this.log.end();
    }

    /**
     * Fill the container configuration with its topology part. The
     * {@code containerConfiguration} and {@code topology} fields must be non
     * {@code null}.
     * 
     * @param container
     *            name The local container name
     * @throws ConfigurationException
     *             Configuration consistency error
     */
    private void fillLocalTopology(String containerName) throws ConfigurationException {
        this.log.start();

        Container localContainer = TopologyHelper.findContainer(containerName, this.topology);

        if (localContainer == null) {
            throw new ConfigurationException(
                    "Failed to retrieve the container configuration for the container named '"
                            + containerName + "'");
        }

        ContainerConfigurationHelper.fill(this.containerConfiguration, localContainer);
        this.containerConfiguration.setName(containerName);
        this.containerConfiguration.setSubdomainName(TopologyHelper.findSubdomain(localContainer,
                this.topology).getName());

        this.log.end();
    }

    /**
     * Fill the sub-domain configuration with its topology part. The
     * {@code subDomainConfiguration} and {@code topology} fields must be non
     * {@code null}.
     * 
     * @param subDomainName
     *            The sub-domain name. Must be non null.
     * @throws ConfigurationException
     *             Configuration consistency error
     */
    private void fillSubDomainTopology(final String subDomainName) throws ConfigurationException {
        this.log.start();

        Subdomain subDomain = TopologyHelper.findSubdomain(subDomainName, this.topology);

        if (subDomain == null) {
            throw new ConfigurationException(
                    "Failed to retrieve the sub-domain configuration for the sub-domain named '"
                            + subDomainName + "'");
        }

        this.subDomainConfiguration.setName(subDomainName);
        this.subDomainConfiguration.setDescription(subDomain.getDescription());
        if (subDomain.isNetworkTimeSynchronized() != null) {
            this.subDomainConfiguration.setNetworkTimeSynchronized(subDomain
                    .isNetworkTimeSynchronized().booleanValue());
        }

        this.log.end();
    }

    /**
     * <p>
     * Post validation on the configuration, once the topology and properties
     * are filled:
     * <ul>
     * <li>check the coherence of the configuration,</li>
     * <li>create required directories if needed.</li>
     * </ul>
     * </p>
     * 
     * @throws ConfigurationException
     */
    private void postValidate() throws ConfigurationException {

        if (RouterService.FAST_POLICY.equals(this.containerConfiguration.getRouterQOS())
                && (this.containerConfiguration.getTCPPort() == 0)) {
            throw new ConfigurationException("The router transport QOS '"
                    + RouterService.FAST_POLICY
                    + "' is not compatible with the transport protocols configuration");
        }
        if (RouterService.SECURE_POLICY.equals(this.containerConfiguration.getRouterQOS())) {
            throw new ConfigurationException("The default router QOS '"
                    + RouterService.SECURE_POLICY
                    + "' is not compatible with the transport protocols configuration");
        }

        final String workingAreaValue = this.containerConfiguration.getWorkDirectoryPath();
        final File workingArea = new File(workingAreaValue);
        if (!workingArea.exists()) {
            if (!workingArea.mkdirs()) {
                this.log.warning(String
                        .format("Unable to create the working area '%s'. Potential problems can occurs later.",
                                workingAreaValue));
            }
        } else if (!workingArea.isDirectory()) {
            throw new ConfigurationException(String.format(
                    "Invalid working area '%s' (not a directory).", workingAreaValue));
        }
    }

    private synchronized void removeSubdomainContainers(String removedSubdomainName)
            throws ConfigurationException {

        for (Map.Entry<String, ContainerConfiguration> containerEntry : this.containersConfig
                .entrySet()) {
            if (containerEntry.getValue().getSubdomainName().equals(removedSubdomainName)) {
                this.containersConfig.remove(containerEntry.getKey());
            }
        }
    }

    /**
     * Validate the consistency of the static topology
     * 
     * @throws ConfigurationException
     */
    private void validateStaticTopology() throws ConfigurationException {
        this.log.start();

        // Check all the sub domains names
        Collection<String> subdomainNameList = new HashSet<String>();
        for (Subdomain subdomain : this.topology.getDomain().getSubDomain()) {
            if (!subdomainNameList.add(subdomain.getName())) {
                throw new ConfigurationException("Duplicate sub-domain name '"
                        + subdomain.getName() + "'");
            }
        }

        // Check all the containers
        Collection<String> nameList = new HashSet<String>();
        Map<String, HashSet<Integer>> portMap = new Hashtable<String, HashSet<Integer>>();
        for (Container container : TopologyHelper.findAllContainers(this.topology)) {
            if (container.getHost() == null) {
                throw new ConfigurationException("The container named '" + container.getName()
                        + "' must declare its host element in static distributed mode");
            }

            HashSet<Integer> portList;
            if (!portMap.containsKey(container.getHost())) {
                portList = new HashSet<Integer>();
                portMap.put(container.getHost(), portList);
            } else {
                portList = portMap.get(container.getHost());
            }

            if ((container.getWebserviceService() != null)
                    && (container.getWebserviceService().getPort() != 0)) {
                if (!portList.add(container.getWebserviceService().getPort())) {
                    throw new ConfigurationException("Duplicate port "
                            + container.getWebserviceService().getPort() + " on host '"
                            + container.getHost() + "'");
                }
            }

            if ((container.getRegistryService() != null)
                    && (container.getRegistryService().getPort() != null)) {
                if (!portList.add(container.getRegistryService().getPort())) {
                    throw new ConfigurationException("Duplicate port "
                            + container.getWebserviceService().getPort() + " on host '"
                            + container.getHost() + "'");
                }
            }

            if (container.getJmxService().getRmiPort() != 0) {
                if (!portList.add(container.getJmxService().getRmiPort())) {
                    throw new ConfigurationException("Duplicate port "
                            + container.getJmxService().getRmiPort() + " on host '"
                            + container.getHost() + "'");
                }
            }

            if ((container.getTransportService() != null)
                    && (container.getTransportService().getTcpPort() >= 0)) {
                if (!portList.add(container.getTransportService().getTcpPort())) {
                    throw new ConfigurationException("Duplicate port "
                            + container.getTransportService().getTcpPort() + " on host '"
                            + container.getHost() + "'");
                }
            }

            if (!nameList.add(container.getName())) {
                throw new ConfigurationException("Duplicate container name '" + container.getName()
                        + "'");
            }
        }

        this.log.end();
    }

    /**
     * Validate the consistency of the topology. The {@code topology} and the
     * {@code containerConfiguration} fields must be non {@code null}. Moreover,
     * if the domain is a standalone domain, {@code containerName} and
     * {@code subDomainName} are updated according to the topology.
     * 
     * @param containerName
     *            The name of the local container retrieved from the
     *            environment, can be {@code null}
     * @return The container name and the sub-domain name of the provided
     *         container.
     * @throws ConfigurationException
     */
    private SubDomainAndContainerNames validateTopology(final String containerName)
            throws ConfigurationException {
        this.log.start();

        final SubDomainAndContainerNames resultValues = new SubDomainAndContainerNames();

        final Domain domain = this.topology.getDomain();
        final List<Subdomain> subdomainList = domain.getSubDomain();
        final DomainMode domainMode = domain.getMode();

        // Validate the STATIC topology
        if (domainMode.equals(DomainMode.STATIC) || domainMode.equals(DomainMode.DYNAMIC)) {

            // Checks if the container name has been set in the
            // server local configuration (ie. content of 'server.properties'
            // or equivalent)
            if (containerName == null) {
                throw new ConfigurationException(
                        "The property "
                                + ConfigurationProperties.CONTAINER_NAME
                                + " must be set in the server local configuration (ie. in the content of file 'server.properties' or equivalent) while being in distributed mode");
            }
            // We know the container name

            // Checks if the current container name can be found in the
            // topology
            if (TopologyHelper.findContainer(containerName, this.topology) == null) {
                throw new ConfigurationException("Cannot find the definition for the container "
                        + containerName + " in the topology.");
            }
            // the container name has been found in the topology

            this.validateStaticTopology();

            resultValues.containerName = containerName;

        } // Woops :-)
        else {
            throw new ConfigurationException("Unsupported domain mode");
        }

        resultValues.subDomainName = TopologyHelper.findSubdomain(
                TopologyHelper.findContainer(resultValues.containerName, this.topology),
                this.topology).getName();

        // validate JNDI consistency
        final Jndi jndi = domain.getJndi();
        if (jndi != null) {
            if ("".equals(jndi.getFactory().trim())) {
                throw new ConfigurationException(
                        "JNDI factory classname must be set to reference an external JNDI server");
            }
            if (jndi.getProviderUrl() == null) {
                throw new ConfigurationException(
                        "JNDI Provider URL must be set as an JNDI server factory is set '");
            }
        }

        // validate containers configuration against the domain mode
        if (DomainMode.STATIC.equals(domainMode) || DomainMode.DYNAMIC.equals(domainMode)) {
            Collection<Container> containers = TopologyHelper.findAllContainers(this.topology);
            for (Container container : containers) {
                if (container.getTransportService() == null) {
                    throw new ConfigurationException(
                            "Transport service must be defined in the container '"
                                    + container.getName() + "' configuration");
                }
            }
        }

        // Yeap I know code below is dirty and inefficient...the whole topology
        // / configuration needs to be heavily refactored
        for (Subdomain subdomain : subdomainList) {
            SubdomainMode subdomainMode = subdomain.getMode();

            boolean hasMasterNode = false;

            List<Container> subdomainContainersList = subdomain.getContainer();
            for (Container container : subdomainContainersList) {

                NodeType containerType = container.getType();
                if (subdomainMode.equals(SubdomainMode.FLOODING)
                        && !containerType.equals(NodeType.PEER)) {
                    StringBuilder errorSB = new StringBuilder();
                    errorSB.append("When a subdomain is in flooding mode, all its ");
                    errorSB.append("containers must be of \"peer\" type");
                    throw new ConfigurationException(errorSB.toString());
                }

                if (subdomainMode.equals(SubdomainMode.STANDALONE)
                        && !containerType.equals(NodeType.STANDALONE)) {
                    StringBuilder errorSB = new StringBuilder();
                    errorSB.append("When a subdomain is in standalone mode, the container ");
                    errorSB.append("must be in of \"standalone\" type");
                    throw new ConfigurationException(errorSB.toString());
                }

                if (subdomainMode.equals(SubdomainMode.MASTER_SLAVE)
                        && !(containerType.equals(NodeType.MASTER) || containerType
                                .equals(NodeType.SLAVE))) {
                    StringBuilder errorSB = new StringBuilder();
                    errorSB.append("When a subdomain is in master-slave mode, ");
                    errorSB.append("all its container must be either of ");
                    errorSB.append("\"master\" or \"slave\" type");
                    throw new ConfigurationException(errorSB.toString());
                }

                if (containerType.equals(NodeType.MASTER)) {
                    if (hasMasterNode) {
                        StringBuilder errorSB = new StringBuilder();
                        errorSB.append("Master node already defined for this subdomain");
                        throw new ConfigurationException(errorSB.toString());
                    } else {
                        hasMasterNode = true;
                    }
                }
            }

            if ((!hasMasterNode) && subdomainMode.equals(SubdomainMode.MASTER_SLAVE)) {
                StringBuilder errorSB = new StringBuilder();
                errorSB.append("No master node found for the current master-slave subdomain");
                throw new ConfigurationException(errorSB.toString());
            }
        }

        this.log.end();
        return resultValues;
    }

    /**
     * Verify the given new container configuration against the topology.
     * 
     * @param newContainerConfiguration
     * @throws InconsistentConfigurationException
     */
    private void verifyContainer(ContainerConfiguration newContainerConfiguration)
            throws InconsistentConfigurationException {

        this.log.debug("Check the container configuration of the new Container.");
        if (DomainMode.STATIC.equals(this.topology.getDomain().getMode())) {
            Container newContainer = TopologyHelper.findContainer(
                    newContainerConfiguration.getName(), this.topology);

            if (newContainer == null) {
                throw new ContainerUnknownException(newContainerConfiguration.getName());
            }
            this.log.debug("Check container " + newContainer.getName() + " ...");
            if (!StringHelper.equal(newContainer.getUser(), newContainerConfiguration.getUser())) {
                throw new InconsistentConfigurationException("User mismatch");
            }
            if (!StringHelper.equal(newContainer.getPassword(),
                    newContainerConfiguration.getPassword())) {
                throw new InconsistentConfigurationException("Password mismatch");
            }

            // JMX Service
            if (newContainer.getJmxService().getRmiPort() != newContainerConfiguration
                    .getJmxRMIConnectorPort()) {
                throw new InconsistentConfigurationException("JMX RMI connector port mismatch");
            }

            // Transport service
            if (newContainer.getTransportService() == null) {
                throw new InconsistentConfigurationException(
                        "Can't find transport service configuration");
            }
            if (newContainer.getTransportService().getTcpPort() == 0) {
                throw new InconsistentConfigurationException("Tranport service TCP port not set");
            }
            if (newContainer.getTransportService().getTcpPort() != newContainerConfiguration
                    .getTCPPort()) {
                throw new InconsistentConfigurationException("TCP port mismatch");
            }
        } else if (newContainerConfiguration.getHost()
                .equals(this.containerConfiguration.getHost())) {
            if (newContainerConfiguration.getName().equals(this.containerConfiguration.getName())) {
                throw new InconsistentConfigurationException("Duplicate container name '"
                        + this.containerConfiguration.getName() + "'");
            }

        }

        this.log.debug("Container configuration of the new Container is OK");
    }

    /**
     * Verify the given new domain configuration against the topology.
     * 
     * @param newDomainConfiguration
     * @throws InconsistentConfigurationException
     */
    private void verifyDomain(DomainConfiguration newDomainConfiguration)
            throws InconsistentConfigurationException {
        this.log.debug("Check the domain configuration of the new Container...");

        if (!newDomainConfiguration.getMode().equals(this.domainConfiguration.getMode())) {
            throw new InconsistentDomainModeConfigurationException(
                    this.domainConfiguration.getMode(), newDomainConfiguration.getMode());
        }

        // JNDI service
        if (!StringHelper.equal(newDomainConfiguration.getJndiConfiguration().getJndiFactory(),
                this.domainConfiguration.getJndiConfiguration().getJndiFactory())) {
            throw new InconsistentConfigurationException("JNDI factory mismatch");
        }
        if (((newDomainConfiguration.getJndiConfiguration().getJndiProviderUrl() == null) && (this.domainConfiguration
                .getJndiConfiguration().getJndiProviderUrl() != null))
                || ((newDomainConfiguration.getJndiConfiguration().getJndiProviderUrl() != null) && (this.domainConfiguration
                        .getJndiConfiguration().getJndiProviderUrl() == null))
                || ((newDomainConfiguration.getJndiConfiguration().getJndiProviderUrl() != null)
                        && (this.domainConfiguration.getJndiConfiguration().getJndiProviderUrl() != null) && newDomainConfiguration
                        .getJndiConfiguration().getJndiProviderUrl().toString()
                        .equals(this.domainConfiguration.toString()))) {
            throw new InconsistentConfigurationException("JNDI Provider Url mismatch");
        }
        if (!StringHelper.equal(newDomainConfiguration.getJndiConfiguration()
                .getJndiSecurityPrincipal(), this.domainConfiguration.getJndiConfiguration()
                .getJndiSecurityPrincipal())) {
            throw new InconsistentConfigurationException("JNDI Security Principal mismatch");
        }
        if (!StringHelper.equal(newDomainConfiguration.getJndiConfiguration()
                .getJndiSecurityCredentials(), this.domainConfiguration.getJndiConfiguration()
                .getJndiSecurityCredentials())) {
            throw new InconsistentConfigurationException("JNDI Security Credentials mismatch");
        }
        if (newDomainConfiguration.getJndiConfiguration().getJndiPoolSize() != this.domainConfiguration
                .getJndiConfiguration().getJndiPoolSize()) {
            throw new InconsistentConfigurationException("JNDI Pool Size mismatch");
        }
        if (newDomainConfiguration.getJndiConfiguration().getJndiBatchSize() != this.domainConfiguration
                .getJndiConfiguration().getJndiBatchSize()) {
            throw new InconsistentConfigurationException("JNDI Batch Size mismatch");
        }

        this.log.debug("Domain configuration of the new Container is OK");
    }

    /**
     * Verify the given new sub-domain configuration against the topology.
     * 
     * @param newSubDomainConfiguration
     * @throws InconsistentConfigurationException
     */
    private void verifySubDomain(final SubDomainConfiguration newSubDomainConfiguration)
            throws InconsistentConfigurationException {
        this.log.debug("Check the sub-domain configuration of the new Container...");

        if (DomainMode.STATIC.equals(this.topology.getDomain().getMode())) {
            Subdomain newSubdomain = TopologyHelper.findSubdomain(
                    newSubDomainConfiguration.getName(), this.topology);

            if (newSubdomain == null) {
                throw new SubdomainUnknownException(newSubDomainConfiguration.getName());
            }
            if (newSubdomain.isNetworkTimeSynchronized() == null) {
                if (newSubDomainConfiguration.isNetworkTimeSynchronized() == true) {
                    throw new InconsistentNetworkTimeSyncException(false, true);
                }
            } else if (newSubdomain.isNetworkTimeSynchronized().booleanValue() != newSubDomainConfiguration
                    .isNetworkTimeSynchronized()) {
                throw new InconsistentNetworkTimeSyncException(newSubdomain
                        .isNetworkTimeSynchronized().booleanValue(),
                        newSubDomainConfiguration.isNetworkTimeSynchronized());
            }
        } else if (newSubDomainConfiguration.getName()
                .equals(this.subDomainConfiguration.getName())) {
            if (newSubDomainConfiguration.isNetworkTimeSynchronized() != this.subDomainConfiguration
                    .isNetworkTimeSynchronized()) {
                throw new InconsistentNetworkTimeSyncException(
                        this.subDomainConfiguration.isNetworkTimeSynchronized(),
                        newSubDomainConfiguration.isNetworkTimeSynchronized());
            }
        }

        this.log.debug("Sub-domain configuration of the new Container is OK");
    }
}
