Java tutorial
/******************************************************************************* * * 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); } }