/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.workflow.execution.api; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicBoolean; import de.rcenvironment.core.component.workflow.api.WorkflowConstants; import de.rcenvironment.core.component.workflow.execution.spi.MultipleWorkflowsStateChangeListener; import de.rcenvironment.core.component.workflow.execution.spi.SingleWorkflowStateChangeListener; import de.rcenvironment.core.notification.DefaultNotificationSubscriber; import de.rcenvironment.core.notification.Notification; import de.rcenvironment.core.notification.NotificationSubscriber; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription; /** * Subscriber for {@link WorkflowState} notifications. * * @author Doreen Seider */ public class WorkflowStateNotificationSubscriber extends DefaultNotificationSubscriber { private static final long serialVersionUID = 421042056359014273L; private static final transient long IS_ALIVE_CHECK_INTERVAL_MSEC = 20000; private final transient boolean considersMultipleWorkflows; private transient MultipleWorkflowsStateChangeListener multiWfStateChangeListener; private transient SingleWorkflowStateChangeListener singleWfStateChangeListener; private transient String singleWfExecutionId; private transient volatile long latestIsAliveReceived = 0; private transient ScheduledFuture<?> isWorkflowAliveCheckTask = null; private AtomicBoolean isStopped = new AtomicBoolean(false); public WorkflowStateNotificationSubscriber(MultipleWorkflowsStateChangeListener listener) { this.multiWfStateChangeListener = listener; considersMultipleWorkflows = true; } public WorkflowStateNotificationSubscriber(SingleWorkflowStateChangeListener listener, String wfExecutionId) { this.singleWfStateChangeListener = listener; this.singleWfExecutionId = wfExecutionId; considersMultipleWorkflows = false; } @Override public Class<?> getInterface() { return NotificationSubscriber.class; } @Override public void processNotification(Notification notification) { if (notification.getHeader().getNotificationIdentifier().equals(WorkflowConstants.NEW_WORKFLOW_NOTIFICATION_ID)) { onWorkflowStateChanged((String) notification.getBody(), WorkflowState.INIT); } else if (WorkflowState.isWorkflowStateValid((String) notification.getBody())) { WorkflowState workflowState = WorkflowState.valueOf((String) notification.getBody()); String wfExecutionId = extractWorkflowIdFromNotificationId(notification); onWorkflowStateChanged(wfExecutionId, workflowState); } } private void onWorkflowStateChanged(String wfExecutionId, WorkflowState newWorkflowState) { if (!newWorkflowState.equals(WorkflowState.IS_ALIVE)) { if (considersMultipleWorkflows) { multiWfStateChangeListener.onWorkflowStateChanged(wfExecutionId, newWorkflowState); } else { singleWfStateChangeListener.onWorkflowStateChanged(newWorkflowState); } } if (!considersMultipleWorkflows) { if (FinalWorkflowState.isFinalWorkflowState(newWorkflowState)) { stopCheckingForWorkflowNotAlive(); } else if (!newWorkflowState.equals(WorkflowState.DISPOSING) && !newWorkflowState.equals(WorkflowState.DISPOSED)) { startCheckingForWorkflowNotAlive(); } if (newWorkflowState.equals(WorkflowState.IS_ALIVE)) { latestIsAliveReceived = System.currentTimeMillis(); } } } private String extractWorkflowIdFromNotificationId(Notification notification) { String topic = notification.getHeader().getNotificationIdentifier(); return topic.replace(WorkflowConstants.STATE_NOTIFICATION_ID, ""); } /** * Starts to check, that {@link WorkflowState#IS_ALIVE} is received continuously. If messages * stop, {@link SingleWorkflowsStateChangeListener#onWorkflowNotAliveAnymore()} is called. */ private synchronized void startCheckingForWorkflowNotAlive() { if (isWorkflowAliveCheckTask == null) { latestIsAliveReceived = System.currentTimeMillis(); isWorkflowAliveCheckTask = ConcurrencyUtils.getAsyncTaskService().scheduleAtFixedRate(new Runnable() { @TaskDescription("Check workflow is alive") @Override public void run() { if (!isStopped.get()) { if (System.currentTimeMillis() - latestIsAliveReceived > IS_ALIVE_CHECK_INTERVAL_MSEC) { isStopped.set(true); String errorMessage = StringUtils.format( "Receiving 'is alive' message from workflow '%s' stopped. Most likely, " + "because the network connection to the workflow host node was interrupted", singleWfExecutionId); singleWfStateChangeListener.onWorkflowNotAliveAnymore(errorMessage); ConcurrencyUtils.getAsyncTaskService().submit(new Runnable() { @TaskDescription("Stop checking workflow is alive") @Override public void run() { stopCheckingForWorkflowNotAlive(); } }); } } } }, IS_ALIVE_CHECK_INTERVAL_MSEC); } } /** * Stops to check, that {@link WorkflowState#IS_ALIVE} is received continuously. */ private synchronized void stopCheckingForWorkflowNotAlive() { if (isWorkflowAliveCheckTask != null) { isWorkflowAliveCheckTask.cancel(false); } } }