org.openspaces.admin.internal.pu.DefaultProcessingUnit.java Source code

Java tutorial

Introduction

Here is the source code for org.openspaces.admin.internal.pu.DefaultProcessingUnit.java

Source

/*******************************************************************************
 * 
 * Copyright (c) 2012 GigaSpaces Technologies Ltd. All rights reserved
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *  
 ******************************************************************************/
package org.openspaces.admin.internal.pu;

import com.gigaspaces.admin.quiesce.InstancesQuiesceState;
import com.gigaspaces.admin.quiesce.QuiesceState;
import com.gigaspaces.grid.gsm.PUDetails;
import com.gigaspaces.internal.quiesce.InternalQuiesceDetails;
import com.gigaspaces.internal.utils.StringUtils;
import com.gigaspaces.internal.utils.collections.ConcurrentHashSet;
import com.j_spaces.kernel.time.SystemTime;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jini.rio.core.RequiredDependencies;
import org.jini.rio.monitor.ProvisionLifeCycleEvent;
import org.openspaces.admin.Admin;
import org.openspaces.admin.AdminException;
import org.openspaces.admin.StatisticsMonitor;
import org.openspaces.admin.application.Application;
import org.openspaces.admin.esm.ElasticServiceManager;
import org.openspaces.admin.gsa.GridServiceAgent;
import org.openspaces.admin.gsc.GridServiceContainer;
import org.openspaces.admin.gsm.GridServiceManager;
import org.openspaces.admin.internal.admin.InternalAdmin;
import org.openspaces.admin.internal.application.InternalApplication;
import org.openspaces.admin.internal.esm.InternalElasticServiceManager;
import org.openspaces.admin.internal.esm.InternalElasticServiceManagers;
import org.openspaces.admin.internal.gsm.InternalGridServiceManager;
import org.openspaces.admin.internal.gsm.InternalGridServiceManagers;
import org.openspaces.admin.internal.pu.dependency.DefaultProcessingUnitDependencies;
import org.openspaces.admin.internal.pu.dependency.InternalProcessingUnitDependencies;
import org.openspaces.admin.internal.pu.dependency.InternalProcessingUnitDependency;
import org.openspaces.admin.internal.pu.events.*;
import org.openspaces.admin.internal.pu.statistics.InternalProcessingUnitStatistics;
import org.openspaces.admin.pu.*;
import org.openspaces.admin.pu.dependency.ProcessingUnitDependencies;
import org.openspaces.admin.pu.dependency.ProcessingUnitDependency;
import org.openspaces.admin.pu.elastic.config.ScaleStrategyConfig;
import org.openspaces.admin.pu.events.*;
import org.openspaces.admin.quiesce.QuiesceDetails;
import org.openspaces.admin.quiesce.QuiesceRequest;
import org.openspaces.admin.quiesce.QuiesceResult;
import org.openspaces.admin.pu.statistics.*;
import org.openspaces.admin.space.Space;
import org.openspaces.admin.zone.config.AnyZonesConfig;
import org.openspaces.admin.zone.config.AtLeastOneZoneConfigurer;
import org.openspaces.admin.zone.config.ExactZonesConfig;
import org.openspaces.admin.zone.config.RequiredZonesConfig;
import org.openspaces.core.properties.BeanLevelProperties;
import org.openspaces.pu.container.support.RequiredDependenciesCommandLineParser;
import org.openspaces.pu.service.ServiceMonitors;
import org.openspaces.pu.sla.SLA;
import org.openspaces.pu.sla.requirement.Requirement;
import org.openspaces.pu.sla.requirement.ZoneRequirement;

import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author kimchy
 */
public class DefaultProcessingUnit implements InternalProcessingUnit {

    private static final Log logger = LogFactory.getLog(DefaultProcessingUnit.class);

    private final InternalProcessingUnits processingUnits;

    private final InternalAdmin admin;

    private final String name;

    private final String simpleName;

    private volatile int numberOfInstances;

    private volatile int numberOfBackups;

    private volatile boolean backupGsmIsInSync;

    private final BeanLevelProperties beanLevelProperties;

    private final SLA sla;

    private final Map<String, String> elasticProperties;

    private volatile DeploymentStatus deploymentStatus = DeploymentStatus.NA;

    private volatile GridServiceManager managingGridServiceManager;

    private final Map<String, GridServiceManager> backupGridServiceManagers = new ConcurrentHashMap<String, GridServiceManager>();

    private final Map<String, ProcessingUnitInstance> processingUnitInstances = new ConcurrentHashMap<String, ProcessingUnitInstance>();

    private final Map<Integer, ProcessingUnitPartition> processingUnitPartitions = new ConcurrentHashMap<Integer, ProcessingUnitPartition>();

    private final ConcurrentMap<String, Space> spaces = new ConcurrentHashMap<String, Space>();

    private final InternalManagingGridServiceManagerChangedEventManager managingGridServiceManagerChangedEventManager;

    private final InternalBackupGridServiceManagerChangedEventManager backupGridServiceManagerChangedEventManager;

    private final InternalProcessingUnitStatusChangedEventManager processingUnitStatusChangedEventManager;

    private final InternalProcessingUnitInstanceAddedEventManager processingUnitInstanceAddedEventManager;

    private final InternalProcessingUnitInstanceRemovedEventManager processingUnitInstanceRemovedEventManager;

    private final InternalProcessingUnitSpaceCorrelatedEventManager spaceCorrelatedEventManager;

    private final InternalProcessingUnitInstanceStatisticsChangedEventManager processingUnitInstanceStatisticsChangedEventManager;

    //map processing unit instance name to it's provision status (mapping is kept even if instance is removed)
    private final Map<String, InternalProvisionStatusHolder> provisionStatus = new HashMap<String, InternalProvisionStatusHolder>();
    private final InternalProcessingUnitInstanceProvisionStatusChangedEventManager processingUnitInstanceProvisionStatusChangedEventManager;

    private final InternalProcessingUnitInstanceMemberAliveIndicatorStatusChangedEventManager processingUnitInstanceMemberAliveIndicatorStatusChangedEventManager;

    private volatile long statisticsInterval = StatisticsMonitor.DEFAULT_MONITOR_INTERVAL;

    private volatile int statisticsHistorySize = StatisticsMonitor.DEFAULT_HISTORY_SIZE;

    private Future<?> scheduledStatisticsMonitor;

    private final ProcessingUnitType processingUnitType;

    /**
     * @deprecated  since 8.0.6 use app.deploy(puDeployment) or gsm.deploy(new ApplicationDeployment(appName,puDeployment) instead.
     */
    private static final String APPLICATION_NAME_CONTEXT_PROPERTY = "com.gs.application";

    /**
     * @deprecated since 8.0.6 use gsm.deploy(puDeployment.addDependency("otherPU")) instead.
     */
    private static final String APPLICATION_DEPENDENCIES_CONTEXT_PROPERTY = "com.gs.application.dependsOn";

    private final String applicationName;

    private volatile InternalApplication application;

    private final InternalProcessingUnitDependencies<ProcessingUnitDependency, InternalProcessingUnitDependency> dependencies;

    private volatile ProcessingUnitStatistics lastStatistics;
    private long lastStatisticsTimestamp;
    private int scheduledStatisticsRefCount = 0;

    private final ConcurrentHashSet<ProcessingUnitStatisticsId> statisticsIds;

    public DefaultProcessingUnit(InternalAdmin admin, InternalProcessingUnits processingUnits, PUDetails details,
            BeanLevelProperties beanLevelProperties) {
        this.admin = admin;
        this.processingUnits = processingUnits;
        this.name = details.getName();
        this.numberOfInstances = details.getNumberOfInstances();
        this.numberOfBackups = details.getNumberOfBackups();

        ProcessingUnitType type = ProcessingUnitType.UNKNOWN;
        try {
            type = ProcessingUnitType.valueOf(details.getType());
        } catch (Exception e) {
            //ignore
        }
        this.processingUnitType = type;

        this.elasticProperties = details.getElasticProperties();

        this.dependencies = new DefaultProcessingUnitDependencies();

        RequiredDependencies instanceDeploymentDependencies = details.getInstanceDeploymentDependencies();
        if (instanceDeploymentDependencies != null) {
            dependencies.addDetailedDependenciesByCommandLineOption(
                    RequiredDependenciesCommandLineParser.INSTANCE_DEPLOYMENT_REQUIRED_DEPENDENCIES_PARAMETER_NAME,
                    instanceDeploymentDependencies);
        }

        RequiredDependencies instanceStartDependencies = details.getInstanceStartDependencies();
        if (instanceStartDependencies != null) {
            dependencies.addDetailedDependenciesByCommandLineOption(
                    RequiredDependenciesCommandLineParser.INSTANCE_START_REQUIRED_DEPENDENCIES_PARAMETER_NAME,
                    instanceStartDependencies);
        }

        this.beanLevelProperties = beanLevelProperties;

        if (details.getApplicationName() != null) {
            applicationName = details.getApplicationName();
        } else {
            //Deprecated
            applicationName = getBeanLevelProperties().getContextProperties()
                    .getProperty(APPLICATION_NAME_CONTEXT_PROPERTY);
        }

        //initialize simpleName
        if (applicationName != null && applicationName.trim().length() > 0) {
            String applNamePrefix = applicationName + ".";
            if (name.startsWith(applNamePrefix)) {
                simpleName = name.substring(applNamePrefix.length());
            } else {
                simpleName = name;
            }
        } else {
            simpleName = name;
        }

        try {
            this.sla = (SLA) details.getSla().get();
        } catch (Exception e) {
            throw new AdminException("Failed to get sla", e);
        }

        if (numberOfBackups == 0) {
            // if we have no backup, its actually just a "single partition"
            processingUnitPartitions.put(0, new DefaultProcessingUnitPartition(this, 0));
        } else {
            for (int i = 0; i < numberOfInstances; i++) {
                processingUnitPartitions.put(i, new DefaultProcessingUnitPartition(this, i));
            }
        }

        this.managingGridServiceManagerChangedEventManager = new DefaultManagingGridServiceManagerChangedEventManager(
                admin, this);
        this.backupGridServiceManagerChangedEventManager = new DefaultBackupGridServiceManagerChangedEventManager(
                admin, this);
        this.processingUnitStatusChangedEventManager = new DefaultProcessingUnitStatusChangedEventManager(admin,
                this);
        this.processingUnitInstanceAddedEventManager = new DefaultProcessingUnitInstanceAddedEventManager(this,
                admin);
        this.processingUnitInstanceRemovedEventManager = new DefaultProcessingUnitInstanceRemovedEventManager(
                admin);
        this.spaceCorrelatedEventManager = new DefaultProcessingUnitSpaceCorrelatedEventManager(this);
        this.processingUnitInstanceStatisticsChangedEventManager = new DefaultProcessingUnitInstanceStatisticsChangedEventManager(
                admin);

        this.processingUnitInstanceProvisionStatusChangedEventManager = new DefaultProcessingUnitInstanceProvisionStatusChangedEventManager(
                admin, this);
        this.processingUnitInstanceMemberAliveIndicatorStatusChangedEventManager = new DefaultProcessingUnitInstanceMemberAliveIndicatorStatusChangedEventManager(
                admin, this);

        this.statisticsIds = new ConcurrentHashSet<ProcessingUnitStatisticsId>();
    }

    @Override
    public ProcessingUnits getProcessingUnits() {
        return this.processingUnits;
    }

    @Override
    public Admin getAdmin() {
        return this.admin;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public BeanLevelProperties getBeanLevelProperties() {
        return beanLevelProperties;
    }

    @Override
    public ProcessingUnitType getType() {
        return processingUnitType;
    }

    @Override
    public ManagingGridServiceManagerChangedEventManager getManagingGridServiceManagerChanged() {
        return this.managingGridServiceManagerChangedEventManager;
    }

    @Override
    public BackupGridServiceManagerChangedEventManager getBackupGridServiceManagerChanged() {
        return this.backupGridServiceManagerChangedEventManager;
    }

    @Override
    public Space getSpace() {
        Iterator<Space> it = spaces.values().iterator();
        if (it.hasNext()) {
            return it.next();
        }
        return null;
    }

    @Override
    public Space[] getSpaces() {
        return this.spaces.values().toArray(new Space[0]);
    }

    @Override
    public void addEmbeddedSpace(Space space) {
        assertStateChangesPermitted();
        Space existingSpace = spaces.putIfAbsent(space.getName(), space);
        if (existingSpace == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Processing Unit " + getName() + " discovered embedded space " + space.getName());
            }
            spaceCorrelatedEventManager
                    .processingUnitSpaceCorrelated(new ProcessingUnitSpaceCorrelatedEvent(space, this));
        }
    }

    @Override
    public boolean removeEmbeddedSpace(Space space) {
        assertStateChangesPermitted();
        return spaces.remove(space.getName()) != null;
    }

    @Override
    public Map<String, String> getElasticProperties() {
        return this.elasticProperties;
    }

    @Override
    public ProcessingUnitStatusChangedEventManager getProcessingUnitStatusChanged() {
        return this.processingUnitStatusChangedEventManager;
    }

    @Override
    public ProcessingUnitInstanceAddedEventManager getProcessingUnitInstanceAdded() {
        return this.processingUnitInstanceAddedEventManager;
    }

    @Override
    public ProcessingUnitInstanceRemovedEventManager getProcessingUnitInstanceRemoved() {
        return this.processingUnitInstanceRemovedEventManager;
    }

    @Override
    public ProcessingUnitSpaceCorrelatedEventManager getSpaceCorrelated() {
        return this.spaceCorrelatedEventManager;
    }

    @Override
    public void addLifecycleListener(ProcessingUnitInstanceLifecycleEventListener eventListener) {
        getProcessingUnitInstanceAdded().add(eventListener);
        getProcessingUnitInstanceRemoved().add(eventListener);
    }

    @Override
    public void removeLifecycleListener(ProcessingUnitInstanceLifecycleEventListener eventListener) {
        getProcessingUnitInstanceAdded().remove(eventListener);
        getProcessingUnitInstanceRemoved().remove(eventListener);
    }

    @Override
    @Deprecated
    public int getNumberOfInstances() {
        return this.numberOfInstances;
    }

    @Override
    public int getPlannedNumberOfPartitions() {
        return getNumberOfInstances();
    }

    @Override
    public int getPlannedNumberOfInstances() {
        if (isElastic() && getType() != ProcessingUnitType.STATEFUL) {
            Integer planned = ((InternalProcessingUnits) this.getProcessingUnits())
                    .getPlannedNumberOfInstancesOfElasticPU(this);
            if (planned != null) {
                return planned;
            }
            // we do not throw an exception since historically #getTotalNumberOfInstances() does not throw exception.
            // we fall-back to an estimate of planned number of instances as reflected by the GSM
        }

        return getTotalNumberOfInstances();
    }

    private boolean isElastic() {
        return !elasticProperties.isEmpty();
    }

    @Override
    public void setNumberOfInstances(int numberOfInstances) {
        assertStateChangesPermitted();
        this.numberOfInstances = numberOfInstances;
    }

    @Override
    public int getNumberOfBackups() {
        return this.numberOfBackups;
    }

    @Override
    public void setNumberOfBackups(int numberOfBackups) {
        assertStateChangesPermitted();
        this.numberOfBackups = numberOfBackups;
    }

    @Deprecated
    @Override
    public int getTotalNumberOfInstances() {
        return getNumberOfInstances() * (getNumberOfBackups() + 1);
    }

    @Override
    public int getMaxInstancesPerVM() {
        return sla.getMaxInstancesPerVM();
    }

    @Override
    public boolean isRequiresIsolation() {
        return sla.isRequiresIsolation();
    }

    @Override
    public int getMaxInstancesPerMachine() {
        return sla.getMaxInstancesPerMachine();
    }

    @Override
    public String getClusterSchema() {
        return sla.getClusterSchema();
    }

    @Override
    public Map<String, Integer> getMaxInstancesPerZone() {
        return Collections.unmodifiableMap(sla.getMaxInstancesPerZone());
    }

    @Override
    public String[] getRequiredZones() {
        ArrayList<String> zones = new ArrayList<String>();
        for (Requirement req : sla.getRequirements()) {
            if (req instanceof ZoneRequirement) {
                zones.add(((ZoneRequirement) req).getZone());
            }
        }
        return zones.toArray(new String[zones.size()]);
    }

    @Override
    public DeploymentStatus getStatus() {
        return this.deploymentStatus;
    }

    @Override
    public boolean waitFor(int numberOfProcessingUnitInstances) {
        return waitFor(numberOfProcessingUnitInstances, admin.getDefaultTimeout(),
                admin.getDefaultTimeoutTimeUnit());
    }

    @Override
    public boolean waitFor(int numberOfProcessingUnitInstances, long timeout, TimeUnit timeUnit) {
        final CountDownLatch latch = new CountDownLatch(numberOfProcessingUnitInstances);
        ProcessingUnitInstanceAddedEventListener added = new ProcessingUnitInstanceAddedEventListener() {
            public void processingUnitInstanceAdded(ProcessingUnitInstance processingUnitInstance) {
                latch.countDown();
            }
        };
        getProcessingUnitInstanceAdded().add(added);
        try {
            return latch.await(timeout, timeUnit);
        } catch (InterruptedException e) {
            return false;
        } finally {
            getProcessingUnitInstanceAdded().remove(added);
        }
    }

    @Override
    public Space waitForSpace() {
        return waitForSpace(admin.getDefaultTimeout(), admin.getDefaultTimeoutTimeUnit());
    }

    @Override
    public Space waitForSpace(long timeout, TimeUnit timeUnit) {
        final CountDownLatch latch = new CountDownLatch(1);
        final AtomicReference<Space> ref = new AtomicReference<Space>();
        ProcessingUnitSpaceCorrelatedEventListener correlated = new ProcessingUnitSpaceCorrelatedEventListener() {
            public void processingUnitSpaceCorrelated(ProcessingUnitSpaceCorrelatedEvent event) {
                ref.set(event.getSpace());
                latch.countDown();
            }
        };
        getSpaceCorrelated().add(correlated);
        try {
            latch.await(timeout, timeUnit);
            Space space = ref.get();
            if (logger.isDebugEnabled()) {
                logger.debug("pu " + getName() + " #waitForSpace() "
                        + (space == null ? "timed out" : " returns space " + space.getName()));
            }
            return space;
        } catch (InterruptedException e) {
            return null;
        } finally {
            getSpaceCorrelated().remove(correlated);
        }
    }

    @Override
    public GridServiceManager waitForManaged() {
        return waitForManaged(admin.getDefaultTimeout(), admin.getDefaultTimeoutTimeUnit());
    }

    @Override
    public GridServiceManager waitForManaged(long timeout, TimeUnit timeUnit) {
        if (isManaged()) {
            return managingGridServiceManager;
        }
        final CountDownLatch latch = new CountDownLatch(1);
        final AtomicReference<GridServiceManager> ref = new AtomicReference<GridServiceManager>();
        ManagingGridServiceManagerChangedEventListener listener = new ManagingGridServiceManagerChangedEventListener() {
            public void processingUnitManagingGridServiceManagerChanged(
                    ManagingGridServiceManagerChangedEvent event) {
                ref.set(event.getNewGridServiceManager());
                latch.countDown();
            }
        };
        getManagingGridServiceManagerChanged().add(listener);
        if (isManaged()) {
            getManagingGridServiceManagerChanged().remove(listener);
            return managingGridServiceManager;
        }
        try {
            latch.await(timeout, timeUnit);
            return ref.get();
        } catch (InterruptedException e) {
            return null;
        } finally {
            getManagingGridServiceManagerChanged().remove(listener);
        }
    }

    @Override
    public boolean canIncrementInstance() {
        return getSpaces().length == 0;
    }

    @Override
    public boolean canDecrementInstance() {
        return getSpaces().length == 0;
    }

    @Override
    public void incrementInstance() {
        if (!isManaged()) {
            throw new AdminException("Processing Unit " + getName() + " does not have an associated managing GSM");
        }
        ((InternalGridServiceManager) managingGridServiceManager).incrementInstance(this);
    }

    @Override
    public void decrementInstance() {
        if (!isManaged()) {
            throw new AdminException("Processing Unit " + getName() + " does not have an associated managing GSM");
        }

        if (decrementPlannedInstances()) {
            return;
        }

        Iterator<ProcessingUnitInstance> it = iterator();
        if (it.hasNext()) {
            ((InternalGridServiceManager) managingGridServiceManager).decrementInstance(it.next());
        }
    }

    @Override
    public GridServiceManager getManagingGridServiceManager() {
        return this.managingGridServiceManager;
    }

    @Override
    public GridServiceManager[] getBackupGridServiceManagers() {
        return this.backupGridServiceManagers.values().toArray(new GridServiceManager[0]);
    }

    @Override
    public boolean isManaged() {
        return managingGridServiceManager != null;
    }

    @Override
    public GridServiceManager getBackupGridServiceManager(String gridServiceManagerUID) {
        return backupGridServiceManagers.get(gridServiceManagerUID);
    }

    @Override
    public boolean undeployAndWait(long timeout, TimeUnit timeUnit) {
        return ((InternalGridServiceManagers) admin.getGridServiceManagers())
                .undeployProcessingUnitsAndWait(new ProcessingUnit[] { this }, timeout, timeUnit);
    }

    @Override
    public void undeployAndWait() {
        undeployAndWait(admin.getDefaultTimeout(), admin.getDefaultTimeoutTimeUnit());
    }

    @Override
    public void undeploy() {
        if (!isManaged()) {
            throw new AdminException("No managing GSM to undeploy from");
        }

        final CountDownLatch latch = new CountDownLatch(1);
        ProcessingUnitRemovedEventListener listener = new ProcessingUnitRemovedEventListener() {
            public void processingUnitRemoved(ProcessingUnit processingUnit) {
                if (getName().equals(processingUnit.getName())) {
                    latch.countDown();
                }
            }
        };

        getProcessingUnits().getProcessingUnitRemoved().add(listener);
        try {
            ((InternalGridServiceManager) managingGridServiceManager).undeployProcessingUnit(getName());
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new AdminException("Failed to undeploy", e);
            }
        } finally {
            getProcessingUnits().getProcessingUnitRemoved().remove(listener);
        }
    }

    @Override
    public void setManagingGridServiceManager(GridServiceManager gridServiceManager) {
        assertStateChangesPermitted();
        this.managingGridServiceManager = gridServiceManager;
    }

    @Override
    public void addManagingGridServiceManager(GridServiceManager gridServiceManager) {
        assertStateChangesPermitted();
        final GridServiceManager previousManaging = (gridServiceManager == this.managingGridServiceManager) ? null
                : this.managingGridServiceManager;
        final GridServiceManager newManaging = gridServiceManager;

        this.managingGridServiceManager = gridServiceManager;
        ManagingGridServiceManagerChangedEvent event = new ManagingGridServiceManagerChangedEvent(this, newManaging,
                previousManaging);
        managingGridServiceManagerChangedEventManager.processingUnitManagingGridServiceManagerChanged(event);
        ((InternalManagingGridServiceManagerChangedEventManager) processingUnits
                .getManagingGridServiceManagerChanged()).processingUnitManagingGridServiceManagerChanged(event);
    }

    @Override
    public void addBackupGridServiceManager(final GridServiceManager backupGridServiceManager) {
        assertStateChangesPermitted();
        GridServiceManager gridServiceManager = this.backupGridServiceManagers
                .put(backupGridServiceManager.getUid(), backupGridServiceManager);
        if (gridServiceManager == null) {
            BackupGridServiceManagerChangedEvent event = new BackupGridServiceManagerChangedEvent(this,
                    BackupGridServiceManagerChangedEvent.Type.ADDED, backupGridServiceManager);
            backupGridServiceManagerChangedEventManager.processingUnitBackupGridServiceManagerChanged(event);
            ((InternalBackupGridServiceManagerChangedEventManager) processingUnits
                    .getBackupGridServiceManagerChanged()).processingUnitBackupGridServiceManagerChanged(event);
        }
    }

    @Override
    public void removeBackupGridServiceManager(String gsmUID) {
        assertStateChangesPermitted();
        final GridServiceManager existingGridServiceManager = backupGridServiceManagers.remove(gsmUID);
        if (existingGridServiceManager != null) {
            BackupGridServiceManagerChangedEvent event = new BackupGridServiceManagerChangedEvent(this,
                    BackupGridServiceManagerChangedEvent.Type.REMOVED, existingGridServiceManager);
            backupGridServiceManagerChangedEventManager.processingUnitBackupGridServiceManagerChanged(event);
            ((InternalBackupGridServiceManagerChangedEventManager) processingUnits
                    .getBackupGridServiceManagerChanged()).processingUnitBackupGridServiceManagerChanged(event);
        }
    }

    @Override
    public boolean setStatus(int statusCode) {
        assertStateChangesPermitted();
        DeploymentStatus tempStatus;
        switch (statusCode) {
        case 0:
            tempStatus = DeploymentStatus.UNDEPLOYED;
            break;
        case 1:
            tempStatus = DeploymentStatus.SCHEDULED;
            break;
        case 2:
            tempStatus = DeploymentStatus.DEPLOYED;
            break;
        case 3:
            tempStatus = DeploymentStatus.BROKEN;
            break;
        case 4:
            tempStatus = DeploymentStatus.COMPROMISED;
            break;
        case 5:
            tempStatus = DeploymentStatus.INTACT;
            break;
        default:
            throw new IllegalStateException("No status match");
        }
        if (tempStatus != deploymentStatus) {
            ProcessingUnitStatusChangedEvent event = new ProcessingUnitStatusChangedEvent(this, deploymentStatus,
                    tempStatus);
            processingUnitStatusChangedEventManager.processingUnitStatusChanged(event);
            if (logger.isDebugEnabled()) {
                logger.debug("ProcessingUnit " + event.getProcessingUnit().getName() + " status changed from "
                        + event.getPreviousStatus() + " to " + event.getNewStatus());
            }
            ((InternalProcessingUnitStatusChangedEventManager) processingUnits.getProcessingUnitStatusChanged())
                    .processingUnitStatusChanged(event);

            deploymentStatus = tempStatus;
            return true;
        }
        deploymentStatus = tempStatus;
        return false;
    }

    @Override
    public Iterator<ProcessingUnitInstance> iterator() {
        return Collections.unmodifiableCollection(processingUnitInstances.values()).iterator();
    }

    @Override
    public ProcessingUnitInstance[] getInstances() {
        return processingUnitInstances.values().toArray(new ProcessingUnitInstance[0]);
    }

    @Override
    public ProcessingUnitInstance[] getProcessingUnitInstances() {
        return getInstances();
    }

    @Override
    public ProcessingUnitPartition[] getPartitions() {
        return processingUnitPartitions.values().toArray(new ProcessingUnitPartition[0]);
    }

    @Override
    public ProcessingUnitPartition getPartition(int partitionId) {
        return processingUnitPartitions.get(partitionId);
    }

    @Override
    public void addProcessingUnitInstance(final ProcessingUnitInstance processingUnitInstance) {
        assertStateChangesPermitted();
        final ProcessingUnitInstance existingProcessingUnitInstance = processingUnitInstances
                .put(processingUnitInstance.getUid(), processingUnitInstance);
        InternalProcessingUnitPartition partition = getPartition(processingUnitInstance);
        if (partition == null) {
            throw new IllegalStateException(
                    "getPartition(processingUnitInstance) returned null for processingUnitInstance.instanceId="
                            + processingUnitInstance.getInstanceId() + " numberOfBackups=" + numberOfBackups
                            + " processingUnitPartitions=" + processingUnitPartitions);
        }
        partition.addProcessingUnitInstance(processingUnitInstance);
        ((InternalProcessingUnitInstance) processingUnitInstance).setProcessingUnitPartition(partition);

        // handle events
        if (existingProcessingUnitInstance == null) {
            processingUnitInstance.setStatisticsInterval(statisticsInterval, TimeUnit.MILLISECONDS);
            processingUnitInstance.setStatisticsHistorySize(statisticsHistorySize);
            if (isMonitoring()) {
                admin.raiseEvent(this, new Runnable() {
                    public void run() {
                        processingUnitInstance.startStatisticsMonitor();
                    }
                });
            }
            processingUnitInstanceAddedEventManager.processingUnitInstanceAdded(processingUnitInstance);
            ((InternalProcessingUnitInstanceAddedEventManager) processingUnits.getProcessingUnitInstanceAdded())
                    .processingUnitInstanceAdded(processingUnitInstance);
            if (application != null) {
                ((InternalProcessingUnitInstanceAddedEventManager) application.getProcessingUnits()
                        .getProcessingUnitInstanceAdded()).processingUnitInstanceAdded(processingUnitInstance);
            }
        }
    }

    @Override
    public void removeProcessingUnitInstance(String uid) {
        final ProcessingUnitInstance processingUnitInstance = processingUnitInstances.remove(uid);
        if (processingUnitInstance != null) {
            admin.scheduleAdminOperation(new Runnable() {

                @Override
                public void run() {
                    while (processingUnitInstance.isMonitoring()) {
                        processingUnitInstance.stopStatisticsMonitor();
                    }
                }
            });

            InternalProcessingUnitPartition partition = getPartition(processingUnitInstance);
            partition.removeProcessingUnitInstance(uid);

            processingUnitInstanceRemovedEventManager.processingUnitInstanceRemoved(processingUnitInstance);
            ((InternalProcessingUnitInstanceRemovedEventManager) processingUnits.getProcessingUnitInstanceRemoved())
                    .processingUnitInstanceRemoved(processingUnitInstance);

            if (application != null) {
                ((InternalProcessingUnitInstanceRemovedEventManager) application.getProcessingUnits()
                        .getProcessingUnitInstanceRemoved()).processingUnitInstanceRemoved(processingUnitInstance);
            }
        }
    }

    private InternalProcessingUnitPartition getPartition(ProcessingUnitInstance processingUnitInstance) {
        InternalProcessingUnitPartition partition;
        if (numberOfBackups == 0) {
            partition = ((InternalProcessingUnitPartition) processingUnitPartitions.get(0));
        } else {
            partition = ((InternalProcessingUnitPartition) processingUnitPartitions
                    .get(processingUnitInstance.getInstanceId() - 1));
        }
        return partition;
    }

    @Override
    public ProcessingUnitInstanceStatisticsChangedEventManager getProcessingUnitInstanceStatisticsChanged() {
        return this.processingUnitInstanceStatisticsChangedEventManager;
    }

    @Override
    public synchronized void setStatisticsInterval(long interval, TimeUnit timeUnit) {
        statisticsInterval = timeUnit.toMillis(interval);
        for (ProcessingUnitInstance processingUnitInstance : processingUnitInstances.values()) {
            processingUnitInstance.setStatisticsInterval(interval, timeUnit);
        }
    }

    @Override
    public void setStatisticsHistorySize(int historySize) {
        this.statisticsHistorySize = historySize;
        for (ProcessingUnitInstance processingUnitInstance : processingUnitInstances.values()) {
            processingUnitInstance.setStatisticsHistorySize(historySize);
        }
    }

    @Override
    public synchronized void startStatisticsMonitor() {

        if (scheduledStatisticsRefCount++ > 0)
            return;

        for (ProcessingUnitInstance processingUnitInstance : processingUnitInstances.values()) {
            processingUnitInstance.startStatisticsMonitor();
        }

        if (scheduledStatisticsMonitor != null) {
            scheduledStatisticsMonitor.cancel(false);
        }

        scheduledStatisticsMonitor = admin.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                ProcessingUnitStatistics stats = DefaultProcessingUnit.this.getStatistics();
                //TODO: Should we store history of both pu instances and pu statistics? Its redundant
                //TODO: Raise event
                //ProcessingUnitStatisticsChangedEvent event = new ProcessingUnitStatisticsChangedEvent(processingUnitInstance, stats);
                //statisticsChangedEventManager.processingUnitInstanceStatisticsChanged(event);                
                //((InternalProcessingUnitStatisticsChangedEventManager) DefaultProcessingUnit.this.getProcessingUnits().getProcessingUnitStatisticsChanged()).processingUnitStatisticsChanged(event);
            }
        }, 0, statisticsInterval, TimeUnit.MILLISECONDS);
    }

    @Override
    public synchronized void stopStatisticsMonitor() {

        if (scheduledStatisticsRefCount != 0 && --scheduledStatisticsRefCount > 0)
            return;

        if (scheduledStatisticsMonitor != null) {
            scheduledStatisticsMonitor.cancel(false);

            for (ProcessingUnitInstance processingUnitInstance : processingUnitInstances.values()) {
                processingUnitInstance.stopStatisticsMonitor();
            }

            scheduledStatisticsMonitor = null;
        }
    }

    @Override
    public synchronized boolean isMonitoring() {
        return scheduledStatisticsMonitor != null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        DefaultProcessingUnit that = (DefaultProcessingUnit) o;
        return name.equals(that.name);
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    private void assertStateChangesPermitted() {
        admin.assertStateChangesPermitted();
    }

    @Override
    public void scale(ScaleStrategyConfig strategyConfig) {

        InternalGridServiceManager gsm = (InternalGridServiceManager) getManagingGridServiceManager();

        if (gsm == null) {
            throw new AdminException("Processing Unit " + getName() + " does not have an associated managing GSM");
        }

        if (admin.getElasticServiceManagers().getSize() != 1) {
            throw new AdminException("ESM server is not running.");
        }

        gsm.setProcessingUnitScaleStrategyConfig(this, strategyConfig);
    }

    @Override
    public void scaleAndWait(ScaleStrategyConfig strategyConfig) {
        scaleAndWait(strategyConfig, admin.getDefaultTimeout(), admin.getDefaultTimeoutTimeUnit());
    }

    @Override
    public boolean scaleAndWait(ScaleStrategyConfig strategyConfig, long timeout, TimeUnit timeunit) {
        long end = SystemTime.timeMillis() + timeunit.toMillis(timeout);
        scale(strategyConfig);

        while (true) {

            ElasticServiceManager[] elasticManagers = ((InternalElasticServiceManagers) admin
                    .getElasticServiceManagers()).getManagersNonFiltered();
            if (elasticManagers.length == 1) {
                InternalElasticServiceManager esm = (InternalElasticServiceManager) elasticManagers[0];
                // we use the nocache method since the local cache does not contain the strategyConfig
                // so the cache may relate to the old startegyConfig
                // see GS-9999 for more details
                if (esm.isManagingProcessingUnitAndScaleNotInProgressNoCache(this)) {
                    //done waiting
                    break;
                }
            }

            long sleepDuration = end - SystemTime.timeMillis();
            if (sleepDuration <= 0) {
                //timeout
                return false;
            }

            try {
                Thread.sleep(Math.min(1000, sleepDuration));
            } catch (InterruptedException e) {
                throw new AdminException("scaleAndWait interrupted", e);
            }
        }
        return true;
    }

    @Deprecated
    public void setElasticProperties(Map<String, String> properties) {
        if (getManagingGridServiceManager() == null) {
            throw new AdminException("Processing Unit " + getName() + " does not have an associated managing GSM");
        }
        ((InternalGridServiceManager) getManagingGridServiceManager()).setProcessingUnitElasticProperties(this,
                properties);

    }

    @Override
    public String getApplicationName() {
        return applicationName;
    }

    @Override
    public Application getApplication() {
        return application;
    }

    public void setApplication(Application application) {
        assertStateChangesPermitted();
        this.application = (InternalApplication) application;
    }

    @Override
    public ScaleStrategyConfig getScaleStrategyConfig() {
        //TODO: Cache the scale config each time a change notification arrives.
        if (getManagingGridServiceManager() == null) {
            throw new AdminException("Processing Unit " + getName() + " does not have an associated managing GSM");
        }
        return ((InternalGridServiceManager) getManagingGridServiceManager())
                .getProcessingUnitScaleStrategyConfig(this);
    }

    @Override
    public boolean decrementPlannedInstances() {
        if (getManagingGridServiceManager() == null) {
            throw new AdminException("Processing Unit " + getName() + " does not have an associated managing GSM");
        }
        return ((InternalGridServiceManager) managingGridServiceManager).decrementPlannedInstances(this);
    }

    /**
     * This method is used by the webui.
     * Need to use getDependencies once Cloudify is migrated to use the dependencies API
     */
    @Override
    public String getApplicationDependencies() {
        List<String> orderedNames = new ArrayList<String>();

        //deprecated: Current way of configuring pu dependencies in Cloudify is by entering a groovy list of the pu names in the format "[a,b,c]"
        String deprecatedDependenciesValue = getBeanLevelProperties().getContextProperties()
                .getProperty(APPLICATION_DEPENDENCIES_CONTEXT_PROPERTY);
        if (deprecatedDependenciesValue != null) {
            String trimmedDeprecatedDependenciesValue = deprecatedDependenciesValue.replace('[', ' ')
                    .replace(']', ' ').trim();
            String[] deprecatedDependencies = StringUtils
                    .delimitedListToStringArray(trimmedDeprecatedDependenciesValue, ",");
            for (String name : deprecatedDependencies) {
                if (!orderedNames.contains(name)) {
                    orderedNames.add(name);
                }
            }
        }

        // Admin API way for configuring pu dependencies
        for (String name : dependencies.getDeploymentDependencies().getRequiredProcessingUnitsNames()) {
            if (!orderedNames.contains(name)) {
                orderedNames.add(name);
            }
        }
        if (orderedNames.isEmpty()) {
            //backwards compatibility
            return "";
        }
        return "[" + StringUtils.collectionToCommaDelimitedString(orderedNames) + "]";
    }

    @Override
    public ProcessingUnitDependencies<ProcessingUnitDependency> getDependencies() {
        return dependencies;
    }

    @Override
    public void processProvisionEvent(ProvisionLifeCycleEvent provisionLifeCycleEvent) {
        ProvisionStatus newStatus;
        switch (provisionLifeCycleEvent.getStatus()) {
        case ProvisionLifeCycleEvent.ALLOCATION_ATTEMPT:
            newStatus = ProvisionStatus.ATTEMPT;
            break;
        case ProvisionLifeCycleEvent.ALLOCATION_SUCCESS:
            newStatus = ProvisionStatus.SUCCESS;
            break;
        case ProvisionLifeCycleEvent.ALLOCATION_FAILURE:
            newStatus = ProvisionStatus.FAILURE;
            break;
        case ProvisionLifeCycleEvent.PENDING_ALLOCATION:
            newStatus = ProvisionStatus.PENDING;
            break;
        default:
            throw new IllegalStateException("unknown provision life-cycle status");
        }
        InternalProvisionStatusHolder provisionStatusHolder = provisionStatus
                .get(provisionLifeCycleEvent.getProcessingUnitInstanceName());
        if (provisionStatusHolder == null) {
            provisionStatusHolder = new InternalProvisionStatusHolder();
        }
        ProvisionStatus lastProvisionStatus = provisionStatusHolder.getNewProvisionStatus();
        if (newStatus != lastProvisionStatus) {
            String processingUnitInstanceName = provisionLifeCycleEvent.getProcessingUnitInstanceName();

            ProcessingUnitInstanceProvisionFailure failure = null;
            final String exceptionMessage = provisionLifeCycleEvent.getException();
            if (exceptionMessage != null) {
                boolean uninstantiable = provisionLifeCycleEvent.isUninstantiable();
                failure = new ProcessingUnitInstanceProvisionFailure(this,
                        provisionLifeCycleEvent.getGscServiceId(), exceptionMessage, uninstantiable);

                //@since 10.1 part of the event ProcessingUnitInstanceProvisionStatusChangedEvent.getProvisionFailure()
                //This reflection can be removed once the deprecated method ProvisionStatus.getProvisionFailure() is removed
                try {
                    Field field = newStatus.getClass().getDeclaredField("provisionFailure");
                    field.setAccessible(true);
                    field.set(newStatus, failure);
                } catch (Exception e) {
                    admin.getAdminLogger().warn("failed to set provision failure " + e);
                }
            }

            ProcessingUnitInstanceProvisionStatusChangedEvent event = new ProcessingUnitInstanceProvisionStatusChangedEvent(
                    this, processingUnitInstanceName, lastProvisionStatus, newStatus,
                    provisionLifeCycleEvent.getGscServiceId(), failure);

            provisionStatusHolder.setNewProvisionStatus(newStatus);
            provisionStatusHolder.setPrevProvisionStatus(lastProvisionStatus);
            provisionStatusHolder.setProvisionFailure(failure);
            provisionStatus.put(provisionLifeCycleEvent.getProcessingUnitInstanceName(), provisionStatusHolder);

            processingUnitInstanceProvisionStatusChangedEventManager
                    .processingUnitInstanceProvisionStatusChanged(event);
            ((InternalProcessingUnitInstanceProvisionStatusChangedEventManager) getProcessingUnits()
                    .getProcessingUnitInstanceProvisionStatusChanged())
                            .processingUnitInstanceProvisionStatusChanged(event);
        }
    }

    @Override
    public Map<String, InternalProvisionStatusHolder> getProvisionStatusPerInstance() {
        return Collections.unmodifiableMap(provisionStatus);
    }

    @Override
    public String getSimpleName() {
        return simpleName;
    }

    @Override
    public ProcessingUnitInstanceProvisionStatusChangedEventManager getProcessingUnitInstanceProvisionStatusChanged() {
        return this.processingUnitInstanceProvisionStatusChangedEventManager;
    }

    @Override
    public ProcessingUnitInstanceMemberAliveIndicatorStatusChangedEventManager getProcessingUnitInstanceMemberAliveIndicatorStatusChanged() {
        return this.processingUnitInstanceMemberAliveIndicatorStatusChangedEventManager;
    }

    @Override
    public synchronized ProcessingUnitStatistics getStatistics() {

        long currentTime = SystemTime.timeMillis();
        if ((currentTime - lastStatisticsTimestamp) < statisticsInterval) {
            if (lastStatistics == null) {
                throw new IllegalStateException("lastStatistics cannot be null here");
            }
            return lastStatistics;
        }

        lastStatisticsTimestamp = currentTime;

        InternalProcessingUnitStatistics statistics = new DefaultProcessingUnitStatistics(currentTime,
                lastStatistics, statisticsHistorySize);
        Map<String, ProcessingUnitInstance> instancesSnapshot = new HashMap<String, ProcessingUnitInstance>(
                processingUnitInstances);

        Set<ProcessingUnitStatisticsId> statisticsIdsSnapshot = new HashSet<ProcessingUnitStatisticsId>(
                statisticsIds);
        for (ProcessingUnitStatisticsId statisticsId : statisticsIdsSnapshot) {
            injectInstanceStatisticsIfAvailable(instancesSnapshot, statistics, statisticsId);
        }

        statistics.calculateStatistics(statisticsIdsSnapshot);

        lastStatistics = statistics;
        return lastStatistics;
    }

    /**
     * Extracts the last statistics specified in statisticsId from the specified instance (or if not specified all instances)
     * and injects it into the puStatistics object
     */
    private void injectInstanceStatisticsIfAvailable(Map<String, ProcessingUnitInstance> instancesSnapshot,
            InternalProcessingUnitStatistics statistics, ProcessingUnitStatisticsId statisticsId) {

        InstancesStatisticsConfig instancesStatistics = statisticsId.getInstancesStatistics();
        if (instancesStatistics instanceof SingleInstanceStatisticsConfig) {
            String instanceUid = ((SingleInstanceStatisticsConfig) statisticsId.getInstancesStatistics())
                    .getInstanceUid();
            ProcessingUnitInstance instance = instancesSnapshot.get(instanceUid);
            if (instance == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to find processing unit " + this.getName() + " instance with UID "
                            + instanceUid);
                }
            } else {
                injectInstanceStatisticsIfAvailable(statistics, statisticsId, instance);
            }
        } else {
            for (ProcessingUnitInstance instance : processingUnitInstances.values()) {
                injectInstanceStatisticsIfAvailable(statistics, statisticsId, instance);
            }
        }
    }

    /**
     * Extracts the last statistics specified in statisticsId from the specified instance and injects it into the puStatistics object
     */
    private void injectInstanceStatisticsIfAvailable(InternalProcessingUnitStatistics puStatistics,
            ProcessingUnitStatisticsId statisticsId, ProcessingUnitInstance instance) {

        ProcessingUnitInstanceStatistics lastStatistics = ((InternalProcessingUnitInstance) instance)
                .getLastStatistics();
        if (lastStatistics == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to retrieve last statistics for for processing unit " + this.getName()
                        + " instance " + instance.getUid());
            }
        } else {
            final ServiceMonitors serviceMonitors = lastStatistics.getMonitors().get(statisticsId.getMonitor());
            if (serviceMonitors == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to find serviceMonitors " + statisticsId.getMonitor()
                            + " for processing unit " + this.getName());
                }
            } else {
                final Object value = serviceMonitors.getMonitors().get(statisticsId.getMetric());
                if (value == null) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("No statistics is available for metric " + statisticsId.getMetric()
                                + " in serviceMonitors " + statisticsId.getMonitor() + " for processing unit "
                                + this.getName() + " serviceMonitors keys are : "
                                + serviceMonitors.getMonitors().keySet());
                    }
                } else {

                    try {
                        ExactZonesConfig zones = getHostingGridServiceAgentZones(instance);

                        //the original statisticsId may contain time or instances aggregation 
                        //we want to inject to the pu statistics the raw metric data
                        final ProcessingUnitStatisticsId newStatisticsId = new ProcessingUnitStatisticsIdConfigurer()
                                .metric(statisticsId.getMetric()).monitor(statisticsId.getMonitor())
                                .timeWindowStatistics(new LastSampleTimeWindowStatisticsConfig()).agentZones(zones)
                                .instancesStatistics(
                                        new SingleInstanceStatisticsConfigurer().instance(instance).create())
                                .create();

                        if (logger.isTraceEnabled()) {
                            logger.trace("adding statistics id " + newStatisticsId + " with value " + value
                                    + " to processing unit statistics of " + getName());
                        }

                        puStatistics.addStatistics(newStatisticsId, value);
                    } catch (AdminException e) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Failed retrieving zones for processing unit instance " + this.getName()
                                    + " : " + e.getMessage());
                        }
                    }
                }
            }
        }
    }

    /**
     * @param processingUnitInstance
     * @return
     */
    public ExactZonesConfig getHostingGridServiceAgentZones(ProcessingUnitInstance processingUnitInstance)
            throws AdminException {

        GridServiceContainer gridServiceContainer = processingUnitInstance.getGridServiceContainer();
        if (gridServiceContainer.getAgentId() != -1) { // an agent started this GSC
            GridServiceAgent agent = gridServiceContainer.getGridServiceAgent();
            if (agent == null) {
                throw new AdminException(
                        "Not yet discovered GSA that started container " + gridServiceContainer.getAgentId());
            } else {
                return agent.getExactZones();
            }
        }
        return new ExactZonesConfig(); // GSC was not started by a GSA
    }

    @Override
    public void addStatisticsCalculation(ProcessingUnitStatisticsId statisticsId) {
        statisticsId.validate();
        this.statisticsIds.add(statisticsId);
    }

    @Override
    public void removeStatisticsCalculation(ProcessingUnitStatisticsId statisticsId) {
        this.statisticsIds.remove(statisticsId);
    }

    @Override
    public Set<ProcessingUnitStatisticsId> getStatisticsCalculations() {
        return Collections.unmodifiableSet(statisticsIds);
    }

    @Override
    public RequiredZonesConfig getRequiredContainerZones() {
        String[] requiredZones = getRequiredZones();
        if (requiredZones.length == 0) {
            return new AnyZonesConfig();
        }
        return new AtLeastOneZoneConfigurer().addZones(requiredZones).create();
    }

    @Override
    public boolean isBackupGsmInSync() {
        return backupGsmIsInSync;
    }

    @Override
    public ProcessingUnitInstance getProcessingUnitInstanceByUid(String uid) {
        return processingUnitInstances.get(uid);
    }

    @Override
    public void setBackupGsmInSync(boolean backupGsmIsInSync) {
        this.backupGsmIsInSync = backupGsmIsInSync;
    }

    @Override
    public QuiesceResult quiesce(QuiesceRequest request) {
        if (!isManaged()) {
            throw new AdminException("No managing GSM to execute quiesce");
        }
        InternalQuiesceDetails quiesceDetails = ((InternalGridServiceManager) managingGridServiceManager)
                .quiesce(this, request);
        return new QuiesceResult(quiesceDetails.getToken(), quiesceDetails.getDescription());
    }

    public void unquiesce(QuiesceRequest request) {
        if (!isManaged()) {
            throw new AdminException("No managing GSM to execute quiesce");
        }
        ((InternalGridServiceManager) managingGridServiceManager).unquiesce(this, request);
    }

    @Override
    public boolean waitFor(QuiesceState desiredState, long timeout, TimeUnit timeUnit) {
        if (!isManaged()) {
            throw new AdminException("No managing GSM to execute quiesce");
        }
        long interval = 1000;
        long expiration;
        // checking long overflow to set the expiration time properly
        BigInteger sum = BigInteger.valueOf(0);
        sum = sum.add(BigInteger.valueOf(System.currentTimeMillis()))
                .add(BigInteger.valueOf(timeUnit.toMillis(timeout)));
        if (sum.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0)
            expiration = Long.MAX_VALUE;
        else
            expiration = sum.longValue();
        for (;;) {
            QuiesceDetails currentDetails = getQuiesceDetails();
            if (instancesReachedQuiesceState(desiredState, currentDetails)) {
                return true;
            }
            try {
                Thread.sleep(interval);
                if (System.currentTimeMillis() >= expiration) {
                    return false;
                }
            } catch (InterruptedException e) {
                return false;
            }
        }
    }

    private boolean instancesReachedQuiesceState(QuiesceState desiredState, QuiesceDetails currentDetails) {
        if (currentDetails.getInstancesQuiesceState() == null)
            return false;

        InstancesQuiesceState instancesQuiesceState = currentDetails.getInstancesQuiesceState();
        // check if planned is equals to actual instances - if there are missing instances ALL instances could not be in desiredState
        if (instancesQuiesceState.getMissingInstancesCount() != 0) {
            return false;
        }
        return currentDetails.getStatus().equals(desiredState)
                && instancesQuiesceState.getFailedToQuiesceInstances().size() == 0;
    }

    @Override
    public boolean waitFor(QuiesceState desiredState) {
        return waitFor(desiredState, admin.getDefaultTimeout(), admin.getDefaultTimeoutTimeUnit());
    }

    @Override
    public QuiesceDetails getQuiesceDetails() {
        if (!isManaged()) {
            throw new AdminException("No managing GSM to execute getQuiesceDetails");
        }
        return ((InternalGridServiceManager) managingGridServiceManager).getQuiesceDetails(this);
    }
}