/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.execution.internal; import java.lang.ref.WeakReference; import java.util.Map; import org.apache.commons.collections4.map.LRUMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; import de.rcenvironment.core.communication.api.CommunicationService; import de.rcenvironment.core.communication.api.PlatformService; import de.rcenvironment.core.communication.common.LogicalNodeId; import de.rcenvironment.core.component.execution.api.ComponentExecutionController; import de.rcenvironment.core.component.execution.api.EndpointDatumSerializer; import de.rcenvironment.core.component.execution.api.ExecutionControllerException; import de.rcenvironment.core.component.execution.api.LocalExecutionControllerUtilsService; import de.rcenvironment.core.component.execution.api.RemotableComponentExecutionControllerService; import de.rcenvironment.core.component.execution.api.RemotableEndpointDatumDispatcher; import de.rcenvironment.core.component.model.endpoint.api.EndpointDatum; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.common.rpc.RemoteOperationException; import de.rcenvironment.core.utils.common.security.AllowRemoteAccess; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallbackExceptionPolicy; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncOrderedExecutionQueue; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncTaskService; /** * Implementation of {@link RemotableEndpointDatumDispatcher}. * * @author Doreen Seider * @author Robert Mischke (id and caching adaptations) */ public class EndpointDatumDispatcherImpl implements EndpointDatumDispatcher, RemotableEndpointDatumDispatcher { private static final String FAILED_TO_SEND_ENDPOINT_DATUM = "Failed to send endpoint datum %s"; private static final Log LOG = LogFactory.getLog(EndpointDatumDispatcherImpl.class); private static final int CACHE_SIZE = 20; private AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); private AsyncOrderedExecutionQueue executionQueue = ConcurrencyUtils.getFactory().createAsyncOrderedExecutionQueue( AsyncCallbackExceptionPolicy.LOG_AND_PROCEED); private Map<String, WeakReference<ComponentExecutionController>> compExeCtrls = new LRUMap<>(CACHE_SIZE); private BundleContext bundleContext; private CommunicationService communicationService; private LocalExecutionControllerUtilsService exeCtrlUtilsService; private PlatformService platformService; private EndpointDatumSerializer endpointDatumSerializer; protected void activate(BundleContext context) { bundleContext = context; } @Override public void dispatchEndpointDatum(final EndpointDatum endpointDatum) { final String executionId = endpointDatum.getInputsComponentExecutionIdentifier(); executionQueue.enqueue(new Runnable() { @Override public void run() { if (platformService.matchesLocalInstance(endpointDatum.getInputsNodeId())) { processEndpointDatum(executionId, endpointDatum); } else { // checking the reachability of the equivalent default logical node id for now; // could be changed once we have active logical node announcements if (communicationService.getReachableLogicalNodes().contains( endpointDatum.getInputsNodeId().convertToDefaultLogicalNodeId())) { forwardEndpointDatum(endpointDatum.getInputsNodeId(), endpointDatum); } else { if (!platformService.matchesLocalInstance(endpointDatum.getWorkflowNodeId())) { forwardEndpointDatum(endpointDatum.getWorkflowNodeId(), endpointDatum); } else { tryToForwardEndpointDatumToActualTarget(); } } } } private void tryToForwardEndpointDatumToActualTarget() { int failureCount = 0; while (true) { if (communicationService.getReachableLogicalNodes().contains(endpointDatum.getInputsNodeId())) { forwardEndpointDatum(endpointDatum.getInputsNodeId(), endpointDatum); ComponentExecutionUtils.logCallbackSuccessAfterFailure(LOG, StringUtils.format("Sending endpoint datum %s", endpointDatum), failureCount); break; } else { if (++failureCount < ComponentExecutionUtils.MAX_RETRIES) { ComponentExecutionUtils.waitForRetryAfterCallbackFailure(LOG, failureCount, StringUtils.format( FAILED_TO_SEND_ENDPOINT_DATUM, endpointDatum), "Target node not reachable: " + endpointDatum.getInputsNodeId()); } else { RemoteOperationException e = new RemoteOperationException("Target node not reachable: " + endpointDatum.getInputsNodeId()); ComponentExecutionUtils.logCallbackFailureAfterRetriesExceeded(LOG, StringUtils.format(FAILED_TO_SEND_ENDPOINT_DATUM, endpointDatum), e); callbackComponentExecutionController(endpointDatum, e); break; } } } } }); } @Override @AllowRemoteAccess public void dispatchEndpointDatum(String serializedEndpointDatum) { dispatchEndpointDatum(endpointDatumSerializer.deserializeEndpointDatum(serializedEndpointDatum)); } protected void forwardEndpointDatum(LogicalNodeId node, EndpointDatum endpointDatum) { // fetching the service proxy on each call, assuming that it will be cached centrally if necessary final RemotableEndpointDatumDispatcher dispatcher = communicationService.getRemotableService(RemotableEndpointDatumDispatcher.class, node); // retrying disabled as long as methods called are not robust against multiple calls // int failureCount = 0; // while (true) { try { dispatcher.dispatchEndpointDatum(endpointDatumSerializer.serializeEndpointDatum(endpointDatum)); // ComponentExecutionUtils.logCallbackSuccessAfterFailure(LOG, StringUtils.format("Sending endpoint datum %s", // endpointDatum), failureCount); // break; } catch (RemoteOperationException e) { // if (++failureCount < ComponentExecutionUtils.MAX_RETRIES) { // ComponentExecutionUtils.waitForRetryAfterCallbackFailure(LOG, failureCount, StringUtils.format( // FAILED_TO_SEND_ENDPOINT_DATUM, endpointDatum), e.toString()); // } else { // ComponentExecutionUtils.logCallbackFailureAfterRetriesExceeded(LOG, StringUtils.format(FAILED_TO_SEND_ENDPOINT_DATUM, // endpointDatum), e); callbackComponentExecutionController(endpointDatum, e); // break; // } } } protected void callbackComponentExecutionController(EndpointDatum endpointDatum, RemoteOperationException e) { if (platformService.matchesLocalInstance(endpointDatum.getOutputsNodeId())) { callbackComponentExecutionControllerLocally(endpointDatum, e); } else { callbackComponentExecutionControllerRemotely(endpointDatum, e); } } private void callbackComponentExecutionControllerLocally(EndpointDatum endpointDatum, RemoteOperationException e) { String executionId = endpointDatum.getOutputsComponentExecutionIdentifier(); ComponentExecutionController compExeCtrl = null; try { compExeCtrl = getComponentExecutionController(executionId); } catch (ExecutionControllerException e1) { LOG.warn(StringUtils.format("Failed to announce that sending endpoint datum '%s'; failed cause: %s", endpointDatum.toString(), e1.toString())); return; } compExeCtrl.onSendingEndointDatumFailed(endpointDatum, e); } private void callbackComponentExecutionControllerRemotely(EndpointDatum endpointDatum, RemoteOperationException e) { String outputCompExeId = endpointDatum.getOutputsComponentExecutionIdentifier(); RemotableComponentExecutionControllerService compExeCtrlService; compExeCtrlService = communicationService.getRemotableService(RemotableComponentExecutionControllerService.class, endpointDatum.getOutputsNodeId()); try { compExeCtrlService.onSendingEndointDatumFailed(outputCompExeId, endpointDatumSerializer.serializeEndpointDatum(endpointDatum), e); } catch (ExecutionControllerException | RemoteOperationException e1) { LOG.warn(StringUtils.format("Failed to announce that sending endpoint datum '%s' failed; cause: %s", endpointDatum, e1.toString())); } } protected void processEndpointDatum(String executionId, EndpointDatum endpointDatum) { ComponentExecutionController compExeCtrl = null; try { compExeCtrl = getComponentExecutionController(executionId); } catch (ExecutionControllerException e) { LOG.warn(StringUtils.format("Endpoint datum '%s' not processed; cause: %s", endpointDatum.toString(), e.toString())); return; } compExeCtrl.onEndpointDatumReceived(endpointDatum); } private ComponentExecutionController getComponentExecutionController(String executionId) throws ExecutionControllerException { ComponentExecutionController compExeCtrl = null; synchronized (compExeCtrls) { if (compExeCtrls.containsKey(executionId)) { compExeCtrl = compExeCtrls.get(executionId).get(); } if (compExeCtrl == null) { compExeCtrl = exeCtrlUtilsService.getExecutionController(ComponentExecutionController.class, executionId, bundleContext); compExeCtrls.put(executionId, new WeakReference<ComponentExecutionController>(compExeCtrl)); } } return compExeCtrl; } protected void bindCommunicationService(CommunicationService newService) { communicationService = newService; } protected void bindLocalExecutionControllerUtilsService(LocalExecutionControllerUtilsService newService) { exeCtrlUtilsService = newService; } protected void bindPlatformService(PlatformService newService) { platformService = newService; } protected void bindEndpointDatumSerializer(EndpointDatumSerializer newService) { endpointDatumSerializer = newService; } }