/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.workflow.execution.internal; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import de.rcenvironment.core.communication.management.WorkflowHostService; import de.rcenvironment.core.component.execution.api.ExecutionConstants; import de.rcenvironment.core.component.execution.api.ExecutionControllerException; import de.rcenvironment.core.component.execution.api.LocalExecutionControllerUtilsService; import de.rcenvironment.core.component.execution.api.WorkflowExecutionControllerCallback; import de.rcenvironment.core.component.workflow.api.WorkflowConstants; import de.rcenvironment.core.component.workflow.execution.api.RemotableWorkflowExecutionControllerService; import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionContext; import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionController; import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionException; import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionInformation; import de.rcenvironment.core.component.workflow.execution.api.WorkflowState; import de.rcenvironment.core.component.workflow.execution.api.WorkflowStateNotificationSubscriber; import de.rcenvironment.core.component.workflow.execution.impl.WorkflowExecutionInformationImpl; import de.rcenvironment.core.component.workflow.execution.spi.SingleWorkflowStateChangeListener; import de.rcenvironment.core.notification.Notification; import de.rcenvironment.core.notification.NotificationService; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.common.rpc.RemoteOperationException; import de.rcenvironment.core.utils.common.security.AllowRemoteAccess; /** * Implementation of {@link RemotableWorkflowExecutionControllerService}. * * @author Doreen Seider * @author Robert Mischke (minor changes) */ public class WorkflowExecutionControllerServiceImpl implements RemotableWorkflowExecutionControllerService { private BundleContext bundleContext; private WorkflowHostService workflowHostService; private LocalExecutionControllerUtilsService exeCtrlUtilsService; private NotificationService notificationService; private Map<String, ServiceRegistration<?>> workflowServiceRegistrations = Collections.synchronizedMap( new HashMap<String, ServiceRegistration<?>>()); private Map<String, WorkflowExecutionInformation> workflowExecutionInformations = Collections.synchronizedMap( new HashMap<String, WorkflowExecutionInformation>()); private final Log log = LogFactory.getLog(getClass()); protected void activate(BundleContext context) { bundleContext = context; } @Override @AllowRemoteAccess public WorkflowExecutionInformation createExecutionController(WorkflowExecutionContext wfExeCtx, Map<String, String> executionAuthTokens, Boolean calledFromRemote) throws WorkflowExecutionException, RemoteOperationException { if (calledFromRemote && !workflowHostService.getLogicalWorkflowHostNodes().contains(wfExeCtx.getNodeId())) { throw new WorkflowExecutionException(StringUtils.format("Workflow execution request refused, as the requested instance is " + "not declared as workflow host: %s", wfExeCtx.getNodeId())); } WorkflowExecutionController workflowController = new WorkflowExecutionControllerImpl(wfExeCtx); workflowController.setComponentExecutionAuthTokens(executionAuthTokens); Dictionary<String, String> properties = new Hashtable<String, String>(); properties.put(ExecutionConstants.EXECUTION_ID_OSGI_PROP_KEY, wfExeCtx.getExecutionIdentifier()); ServiceRegistration<?> serviceRegistration = bundleContext.registerService( new String[] { WorkflowExecutionController.class.getName(), WorkflowExecutionControllerCallback.class.getName() }, workflowController, properties); WorkflowExecutionInformationImpl workflowExecutionInformation = new WorkflowExecutionInformationImpl(wfExeCtx); workflowExecutionInformation.setIdentifier(wfExeCtx.getExecutionIdentifier()); workflowExecutionInformation.setWorkflowState(WorkflowState.INIT); synchronized (workflowExecutionInformations) { workflowExecutionInformations.put(wfExeCtx.getExecutionIdentifier(), workflowExecutionInformation); workflowServiceRegistrations.put(wfExeCtx.getExecutionIdentifier(), serviceRegistration); } return workflowExecutionInformation; } @Override @AllowRemoteAccess public void performDispose(final String wfExecutionId) throws RemoteOperationException { final WorkflowStateNotificationSubscriber workflowStateDisposedListener = new WorkflowStateNotificationSubscriber(new SingleWorkflowStateChangeListener() { @Override public void onWorkflowStateChanged(WorkflowState newState) { if (newState == WorkflowState.DISPOSED) { dispose(wfExecutionId); } else if (newState != WorkflowState.DISPOSING) { log.warn(StringUtils.format( "Received unexpected workflow state '%s' for workflow '%s'", newState.getDisplayName(), wfExecutionId)); } } @Override public void onWorkflowNotAliveAnymore(String errorMessage) {} }, wfExecutionId) { private static final long serialVersionUID = 3168599724769249933L; @Override public void processNotification(Notification notification) { super.processNotification(notification); if (WorkflowState.isWorkflowStateValid((String) notification.getBody()) && (!WorkflowState.valueOf((String) notification.getBody()).equals(WorkflowState.DISPOSING))) { try { notificationService.unsubscribe(WorkflowConstants.STATE_NOTIFICATION_ID + wfExecutionId, this); } catch (RemoteOperationException e) { log.warn(StringUtils.format("Failed to unsubscribe from state changes " + "for workflow %s: %s", wfExecutionId, e.getMessage())); } } } }; try { notificationService.subscribe(WorkflowConstants.STATE_NOTIFICATION_ID + wfExecutionId, workflowStateDisposedListener); } catch (RemoteOperationException e) { log.error("Failed to subscribe for workflow state changes before disposing: " + e.getMessage()); return; // preserve the "old" RTE behavior for now } try { exeCtrlUtilsService.getExecutionController(WorkflowExecutionController.class, wfExecutionId, bundleContext).dispose(); } catch (ExecutionControllerException e) { log.warn(StringUtils.format("Failed to dispose workflow (%s). It seems to be already disposed (%s).", wfExecutionId, e.getMessage())); } synchronized (workflowExecutionInformations) { workflowExecutionInformations.remove(wfExecutionId); } } private void dispose(String executionId) { synchronized (workflowServiceRegistrations) { if (workflowServiceRegistrations.containsKey(executionId)) { workflowServiceRegistrations.get(executionId).unregister(); workflowServiceRegistrations.remove(executionId); } } } @Override @AllowRemoteAccess public void performStart(String executionId) throws ExecutionControllerException, RemoteOperationException { exeCtrlUtilsService.getExecutionController(WorkflowExecutionController.class, executionId, bundleContext).start(); } @Override @AllowRemoteAccess public void performCancel(String executionId) throws ExecutionControllerException, RemoteOperationException { exeCtrlUtilsService.getExecutionController(WorkflowExecutionController.class, executionId, bundleContext).cancel(); } @Override @AllowRemoteAccess public void performPause(String executionId) throws ExecutionControllerException, RemoteOperationException { exeCtrlUtilsService.getExecutionController(WorkflowExecutionController.class, executionId, bundleContext).pause(); } @Override @AllowRemoteAccess public void performResume(String executionId) throws ExecutionControllerException, RemoteOperationException { exeCtrlUtilsService.getExecutionController(WorkflowExecutionController.class, executionId, bundleContext).resume(); } @Override @AllowRemoteAccess public WorkflowState getWorkflowState(String executionId) throws ExecutionControllerException, RemoteOperationException { return exeCtrlUtilsService.getExecutionController(WorkflowExecutionController.class, executionId, bundleContext).getState(); } @Override @AllowRemoteAccess public Long getWorkflowDataManagementId(String executionId) throws ExecutionControllerException { return exeCtrlUtilsService.getExecutionController(WorkflowExecutionController.class, executionId, bundleContext) .getDataManagementId(); } @Override @AllowRemoteAccess public Collection<WorkflowExecutionInformation> getWorkflowExecutionInformations() throws ExecutionControllerException, RemoteOperationException { Map<String, WorkflowExecutionInformation> wfExeInfoSnapshot = null; synchronized (workflowExecutionInformations) { wfExeInfoSnapshot = new HashMap<>(); wfExeInfoSnapshot.putAll(workflowExecutionInformations); } for (String executionId : wfExeInfoSnapshot.keySet()) { WorkflowState state; Long dmId; try { // TODO both of the calls delegate to the workflow execution controller service; should be optimized state = getWorkflowState(executionId); dmId = getWorkflowDataManagementId(executionId); } catch (ExecutionControllerException e) { log.debug(StringUtils.format("Removed workflow %s from temporary set of workflow execution infos: %s", executionId, e.getMessage())); continue; } ((WorkflowExecutionInformationImpl) wfExeInfoSnapshot.get(executionId)) .setWorkflowState(state); ((WorkflowExecutionInformationImpl) wfExeInfoSnapshot.get(executionId)) .setWorkflowDataManagementId(dmId); } return new HashSet<WorkflowExecutionInformation>(wfExeInfoSnapshot.values()); } protected void bindWorkflowHostService(WorkflowHostService newService) { this.workflowHostService = newService; } protected void bindLocalExecutionControllerUtilsService(LocalExecutionControllerUtilsService newService) { exeCtrlUtilsService = newService; } protected void bindNotificationService(NotificationService newService) { this.notificationService = newService; } }