/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.gui.workflow.view.properties; import java.util.Deque; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import de.rcenvironment.core.communication.api.CommunicationService; import de.rcenvironment.core.communication.management.WorkflowHostService; import de.rcenvironment.core.component.api.ComponentConstants; import de.rcenvironment.core.component.model.endpoint.api.EndpointDatum; import de.rcenvironment.core.component.workflow.execution.api.GenericSubscriptionManager; import de.rcenvironment.core.datamodel.api.DataType; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.utils.incubator.ServiceRegistry; import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription; /** * Provides central access to input values, processed and pending ones. * * @author Doreen Seider * @author Robert Mischke (tweaked notification setup) */ public final class InputModel { private static final int MAX_INPUT_COUNT = 25000; private static InputModel instance; private static Map<String, Map<String, Map<String, Deque<EndpointDatum>>>> allInputs; private static InputSubscriptionEventProcessor eventProcessor; private static GenericSubscriptionManager currentInputManager; private static CountDownLatch initialSubscriptionLatch; private InputModel() {} /** * Singleton getter to provide central model access. * * @return the singleton instance */ public static synchronized InputModel getInstance() { if (null == instance) { instance = new InputModel(); initialSubscriptionLatch = new CountDownLatch(1); allInputs = new ConcurrentHashMap<String, Map<String, Map<String, Deque<EndpointDatum>>>>(); eventProcessor = new InputSubscriptionEventProcessor(instance); currentInputManager = new GenericSubscriptionManager(eventProcessor, ServiceRegistry.createAccessFor(instance).getService(CommunicationService.class), ServiceRegistry.createAccessFor(instance).getService(WorkflowHostService.class)); ConcurrencyUtils.getAsyncTaskService().execute(new Runnable() { @Override @TaskDescription("Initial inputs subscriptions") public void run() { currentInputManager .updateSubscriptionsForPrefixes(new String[] { ComponentConstants.NOTIFICATION_ID_PREFIX_PROCESSED_INPUT }); initialSubscriptionLatch.countDown(); } }); } return instance; } /** * Updates subscriptions to known server instances. */ public void updateSubscriptions() { try { initialSubscriptionLatch.await(); } catch (InterruptedException e) { // TODO better handling? throw new RuntimeException("Interrupted while waiting for initial subscriptions to complete", e); } currentInputManager.updateSubscriptionsForPrefixes(new String[] { ComponentConstants.NOTIFICATION_ID_PREFIX_PROCESSED_INPUT }); } /** * Batch version of {@link #addConsoleRow(Input)} to reduce synchronization overhead. * * @param inputs the list of {@link EndpointDatum}s to add */ public synchronized void addInputs(List<EndpointDatum> inputs) { for (EndpointDatum input : inputs) { if (isValue(input)) { String workflowId = input.getWorkflowExecutionIdentifier(); String componentId = input.getInputsComponentExecutionIdentifier(); String inputName = input.getInputName(); if (!allInputs.containsKey(workflowId)) { allInputs.put(workflowId, new HashMap<String, Map<String, Deque<EndpointDatum>>>()); } if (!allInputs.get(workflowId).containsKey(componentId)) { allInputs.get(workflowId).put(componentId, new HashMap<String, Deque<EndpointDatum>>()); } if (!allInputs.get(workflowId).get(componentId).containsKey(inputName)) { allInputs.get(workflowId).get(componentId).put(inputName, new LinkedList<EndpointDatum>()); } if (allInputs.size() > MAX_INPUT_COUNT) { allInputs.get(workflowId).get(componentId).get(inputName).removeFirst(); } allInputs.get(workflowId).get(componentId).get(inputName).addLast(input); } } } /** * Returns a {@link Deque} of {@link EndpointDatum}s for a specified input of a specified component and worklfow. Intended to be called * by input view. * * @param workflowId identifier of workflow * @param componentId identifier of component * @param inputName name of input * @return {@link Deque} containing inputs */ public synchronized Deque<EndpointDatum> getInputs(String workflowId, String componentId, String inputName) { Deque<EndpointDatum> inputs = new LinkedList<>(); if (allInputs.containsKey(workflowId) && allInputs.get(workflowId).containsKey(componentId) && allInputs.get(workflowId).get(componentId).containsKey(inputName)) { inputs = new LinkedList<EndpointDatum>(allInputs.get(workflowId).get(componentId).get(inputName)); } return inputs; } private boolean isValue(EndpointDatum input) { return !input.getValue().getDataType().equals(DataType.Internal); } public InputSubscriptionEventProcessor getEventProcessor() { return eventProcessor; } /** * Ensures that the console model is registered to listen for inputs. */ public static void ensureInputCaptureIsInitialized() { // trigger model creation & subscription if not done yet getInstance(); } }