package io.cattle.platform.engine.manager.impl; import static io.cattle.platform.engine.process.ExitReason.*; import io.cattle.platform.archaius.util.ArchaiusUtil; import io.cattle.platform.engine.context.EngineContext; import io.cattle.platform.engine.manager.ProcessManager; import io.cattle.platform.engine.manager.ProcessNotFoundException; import io.cattle.platform.engine.process.ExecutionExceptionHandler; import io.cattle.platform.engine.process.LaunchConfiguration; import io.cattle.platform.engine.process.ProcessDefinition; import io.cattle.platform.engine.process.ProcessInstance; import io.cattle.platform.engine.process.ProcessInstanceException; import io.cattle.platform.engine.process.ProcessServiceContext; import io.cattle.platform.engine.process.ProcessState; import io.cattle.platform.engine.process.StateChangeMonitor; import io.cattle.platform.engine.process.impl.DefaultProcessInstanceImpl; import io.cattle.platform.engine.server.ProcessInstanceReference; import io.cattle.platform.eventing.EventService; import io.cattle.platform.lock.LockManager; import io.cattle.platform.util.concurrent.DelayedObject; import io.cattle.platform.util.type.InitializationTask; import io.cattle.platform.util.type.NamedUtils; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.DelayQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.apache.cloudstack.managed.context.NoExceptionRunnable; import com.netflix.config.DynamicLongProperty; public class DefaultProcessManager implements ProcessManager, InitializationTask { private static final DynamicLongProperty EXECUTION_DELAY = ArchaiusUtil.getLong("process.log.save.interval.ms"); @Inject ProcessRecordDao processRecordDao; @Inject List<ProcessDefinition> definitionList; Map<String, ProcessDefinition> definitions = new HashMap<String, ProcessDefinition>(); @Inject LockManager lockManager; DelayQueue<DelayedObject<WeakReference<ProcessInstance>>> toPersist = new DelayQueue<DelayedObject<WeakReference<ProcessInstance>>>(); @Inject ScheduledExecutorService executor; @Inject EventService eventService; @Inject ExecutionExceptionHandler exceptionHandler; @Inject List<StateChangeMonitor> changeMonitors; @Override public ProcessInstance createProcessInstance(LaunchConfiguration config) { return createProcessInstance(new ProcessRecord(config, null, null), false, false); } @Override public void scheduleProcessInstance(LaunchConfiguration config) { ProcessInstance pi = createProcessInstance(new ProcessRecord(config, null, null), true, false); try { pi.execute(); } catch (ProcessInstanceException e) { if (e.getExitReason() == SCHEDULED) { return; } else { throw e; } } } protected ProcessInstance createProcessInstance(ProcessRecord record, boolean schedule, boolean replay) { if (record == null) return null; ProcessDefinition processDef = definitions.get(record.getProcessName()); if (processDef == null) throw new ProcessNotFoundException("Failed to find ProcessDefinition for [" + record.getProcessName() + "]"); ProcessState state = processDef.constructProcessState(record); if (state == null) throw new ProcessNotFoundException("Failed to construct ProcessState for [" + record.getProcessName() + "]"); if (record.getId() == null && (schedule || !EngineContext.hasParentProcess())) record = processRecordDao.insert(record); ProcessServiceContext context = new ProcessServiceContext(lockManager, eventService, this, exceptionHandler, changeMonitors); DefaultProcessInstanceImpl process = new DefaultProcessInstanceImpl(context, record, processDef, state, schedule, replay); if (record.getId() != null) queue(process); return process; } @Override public ProcessDefinition getProcessDelegate(ProcessDefinition def) { String delegate = def.getProcessDelegateName(); if (delegate == null) { return null; } ProcessDefinition processDef = definitions.get(delegate); if (processDef == null) throw new ProcessNotFoundException("Failed to find ProcessDefinition for [" + delegate + "]"); return processDef; } @Override public void persistState(ProcessInstance process, boolean schedule) { if (!(process instanceof DefaultProcessInstanceImpl)) { throw new IllegalArgumentException("Can only persist ProcessInstances that are created by this repository"); } DefaultProcessInstanceImpl processImpl = (DefaultProcessInstanceImpl) process; synchronized (processImpl) { ProcessRecord record = processImpl.getProcessRecord(); if (record.getId() != null) processRecordDao.update(record, schedule); } } @Override public List<ProcessInstanceReference> pendingTasks() { return processRecordDao.pendingTasks(); } @Override public Long getRemainingTask(ProcessInstance instance) { if (!(instance instanceof DefaultProcessInstanceImpl)) { return null; } ProcessRecord record = ((DefaultProcessInstanceImpl)instance).getProcessRecord(); if (record == null) { return null; } return processRecordDao.nextTask(record.getResourceType(), record.getResourceId()); } @Override public ProcessInstance loadProcess(Long id) { ProcessRecord record = processRecordDao.getRecord(id); if (record == null) { throw new ProcessNotFoundException("Failed to find ProcessRecord for [" + id + "]"); } return createProcessInstance(record, false, true); } protected void persistInProgress() throws InterruptedException { while (true) { ProcessInstance process = toPersist.take().getObject().get(); if (process == null) { continue; } synchronized (process) { if (process.isRunningLogic()) { persistState(process, false); } } } } protected void queue(ProcessInstance process) { WeakReference<ProcessInstance> ref = new WeakReference<ProcessInstance>(process); toPersist.put(new DelayedObject<WeakReference<ProcessInstance>>(System.currentTimeMillis() + EXECUTION_DELAY.get(), ref)); } @Override public ProcessDefinition getProcessDefinition(String name) { return definitions.get(name); } @Override public void start() { definitions = NamedUtils.createMapByName(definitionList); executor.scheduleAtFixedRate(new NoExceptionRunnable() { @Override public void doRun() throws Exception { /* * This really blocks forever, but just in case it fails we * restart */ persistInProgress(); } }, EXECUTION_DELAY.get(), EXECUTION_DELAY.get(), TimeUnit.MILLISECONDS); } @Override public ProcessInstanceReference loadReference(Long id) { return processRecordDao.loadReference(id); } }