/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.communication.management.internal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.rcenvironment.core.communication.common.InstanceNodeSessionId; import de.rcenvironment.core.communication.common.LogicalNodeId; import de.rcenvironment.core.communication.common.WorkflowHostUtils; import de.rcenvironment.core.communication.configuration.NodeConfigurationService; import de.rcenvironment.core.communication.management.WorkflowHostService; import de.rcenvironment.core.communication.management.WorkflowHostSetListener; import de.rcenvironment.core.communication.nodeproperties.NodePropertiesService; import de.rcenvironment.core.communication.nodeproperties.NodeProperty; import de.rcenvironment.core.communication.nodeproperties.NodePropertyConstants; import de.rcenvironment.core.communication.nodeproperties.spi.NodePropertiesChangeListener; import de.rcenvironment.core.communication.nodeproperties.spi.NodePropertiesChangeListenerAdapter; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.utils.common.service.AdditionalServiceDeclaration; import de.rcenvironment.core.utils.common.service.AdditionalServicesProvider; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallback; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallbackExceptionPolicy; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncOrderedCallbackManager; /** * Default {@link WorkflowHostService} implementation. * * @author Robert Mischke */ // FIXME temporary package location to simplify migration; move to "de.rcenvironment.core.component.workflow.internal" // (or similar) when complete - misc_ro public class WorkflowHostServiceImpl implements WorkflowHostService, AdditionalServicesProvider { private NodeConfigurationService platformService; private NodePropertiesService nodePropertiesService; private final Set<InstanceNodeSessionId> workflowHostsWorkingCopy = new HashSet<InstanceNodeSessionId>(); private Set<InstanceNodeSessionId> workflowHostsSnapshot = Collections.unmodifiableSet(new HashSet<InstanceNodeSessionId>()); private Set<InstanceNodeSessionId> workflowHostsAndSelfSnapshot = Collections.unmodifiableSet(new HashSet<InstanceNodeSessionId>()); private final AsyncOrderedCallbackManager<WorkflowHostSetListener> callbackManager = ConcurrencyUtils.getFactory().createAsyncOrderedCallbackManager(AsyncCallbackExceptionPolicy.LOG_AND_PROCEED); private final Log log = LogFactory.getLog(getClass()); private InstanceNodeSessionId localNodeId; private Set<LogicalNodeId> logicalWorkflowHostsSnapshot; private Set<LogicalNodeId> logicalWorkflowHostsAndSelfSnapshot; /** * OSGi-DS lifecycle method. */ public void activate() { boolean isWorkflowHost = platformService.isWorkflowHost(); nodePropertiesService.addOrUpdateLocalNodeProperty(WorkflowHostUtils.KEY_IS_WORKFLOW_HOST, NodePropertyConstants.wrapBoolean(isWorkflowHost)); localNodeId = platformService.getInstanceNodeSessionId(); // create initial placeholders workflowHostsSnapshot = Collections.unmodifiableSet(new HashSet<InstanceNodeSessionId>()); logicalWorkflowHostsSnapshot = Collections.unmodifiableSet(new HashSet<LogicalNodeId>()); Set<InstanceNodeSessionId> tempWorkflowHostsAndSelf = new HashSet<>(); tempWorkflowHostsAndSelf.add(localNodeId); workflowHostsAndSelfSnapshot = Collections.unmodifiableSet(tempWorkflowHostsAndSelf); Set<LogicalNodeId> tempLogicalWorkflowHostsAndSelf = new HashSet<>(); // note: ok to simply use the local default logical node here as long as workflow hosts are not logical-node-specific tempLogicalWorkflowHostsAndSelf.add(localNodeId.convertToDefaultLogicalNodeId()); logicalWorkflowHostsAndSelfSnapshot = Collections.unmodifiableSet(tempLogicalWorkflowHostsAndSelf); } @Override public Collection<AdditionalServiceDeclaration> defineAdditionalServices() { List<AdditionalServiceDeclaration> result = new ArrayList<AdditionalServiceDeclaration>(); result.add(new AdditionalServiceDeclaration(NodePropertiesChangeListener.class, new NodePropertiesChangeListenerAdapter() { @Override public void onReachableNodePropertiesChanged(Collection<? extends NodeProperty> addedProperties, Collection<? extends NodeProperty> updatedProperties, Collection<? extends NodeProperty> removedProperties) { // delegate to keep synchronization approach simple - misc_ro updateOnReachableNodePropertiesChanged(addedProperties, updatedProperties, removedProperties); } })); // register listener on self result.add(new AdditionalServiceDeclaration(WorkflowHostSetListener.class, new WorkflowHostSetListener() { @Override public void onReachableWorkflowHostsChanged(Set<InstanceNodeSessionId> reachableWfHosts, Set<InstanceNodeSessionId> addedWfHosts, Set<InstanceNodeSessionId> removedWfHosts) { log.debug("List of reachable workflow hosts updated: " + reachableWfHosts); } })); return result; } /** * OSGi-DS bind method. * * @param newInstance the new service instance to bind */ public void bindNodeConfigurationService(NodeConfigurationService newInstance) { this.platformService = newInstance; } /** * OSGi-DS bind method. * * @param newInstance the new service instance to bind */ public void bindNodePropertiesService(NodePropertiesService newInstance) { this.nodePropertiesService = newInstance; } @Override public synchronized Set<InstanceNodeSessionId> getWorkflowHostNodes() { return workflowHostsSnapshot; } @Override public synchronized Set<LogicalNodeId> getLogicalWorkflowHostNodes() { return logicalWorkflowHostsSnapshot; } @Override public synchronized Set<InstanceNodeSessionId> getWorkflowHostNodesAndSelf() { return workflowHostsAndSelfSnapshot; } @Override public synchronized Set<LogicalNodeId> getLogicalWorkflowHostNodesAndSelf() { return logicalWorkflowHostsAndSelfSnapshot; } /** * Registers a {@link WorkflowHostSetListener} for changes to the set of reachable workflow hosts. * * @param listener the new listener */ public synchronized void addWorkflowHostSetListener(WorkflowHostSetListener listener) { // create copy in synchronized block final Set<InstanceNodeSessionId> currentWorkflowHostsCopy = workflowHostsSnapshot; callbackManager.addListenerAndEnqueueCallback(listener, new AsyncCallback<WorkflowHostSetListener>() { @Override public void performCallback(WorkflowHostSetListener listener) { // FIXME implement difference sets or change listener interface listener.onReachableWorkflowHostsChanged(currentWorkflowHostsCopy, null, null); } }); } /** * Unregisters a {@link WorkflowHostSetListener}. * * @param listener the listener to remove */ public void removeWorkflowHostSetListener(WorkflowHostSetListener listener) { callbackManager.removeListener(listener); } private synchronized void updateOnReachableNodePropertiesChanged(Collection<? extends NodeProperty> addedProperties, Collection<? extends NodeProperty> updatedProperties, Collection<? extends NodeProperty> removedProperties) { boolean relevantModification = false; for (NodeProperty property : addedProperties) { if (WorkflowHostUtils.isWorkflowHostProperty(property)) { boolean value = WorkflowHostUtils.getWorkflowHostPropertyValue(property); // added properties are only relevant if the indicate a workflow host if (!value) { continue; } InstanceNodeSessionId nodeId = property.getInstanceNodeSessionId(); boolean setChanged = workflowHostsWorkingCopy.add(nodeId); if (setChanged) { relevantModification = true; log.info("New workflow host available: " + nodeId); } else { log.debug("New workflow host available, but it caused no set modification: " + property); } } } for (NodeProperty property : updatedProperties) { if (WorkflowHostUtils.isWorkflowHostProperty(property)) { InstanceNodeSessionId nodeId = property.getInstanceNodeSessionId(); boolean value = WorkflowHostUtils.getWorkflowHostPropertyValue(property); if (value) { boolean setChanged = workflowHostsWorkingCopy.add(nodeId); if (setChanged) { relevantModification = true; log.info("New workflow host available (by configuration change): " + nodeId); } else { log.debug("New workflow host available (by configuration change), but it caused no set modification: " + property); } } else { boolean setChanged = workflowHostsWorkingCopy.remove(nodeId); if (setChanged) { relevantModification = true; log.info("Node removed as workflow host (by configuration change): " + nodeId); } else { log.debug("Node removed as workflow host (by configuration change), but it caused no set modification: " + property); } } } } for (NodeProperty property : removedProperties) { if (WorkflowHostUtils.isWorkflowHostProperty(property)) { InstanceNodeSessionId nodeId = property.getInstanceNodeSessionId(); // removed properties are only relevant if the node was a workflow host before if (!workflowHostsWorkingCopy.contains(nodeId)) { continue; } boolean setChanged = workflowHostsWorkingCopy.remove(nodeId); if (setChanged) { relevantModification = true; log.info("Workflow host became unavailable: " + nodeId); } else { log.debug("Workflow host was removed, but caused no set modification: " + property); } } } if (relevantModification) { // create new detached copy workflowHostsSnapshot = Collections.unmodifiableSet(new HashSet<InstanceNodeSessionId>(workflowHostsWorkingCopy)); // FIXME >8.0 preliminary - this only supports the *default* logical node ids, not the ones published via other mechanisms logicalWorkflowHostsSnapshot = convertFromInstanceIdsToLogicalNodesSet(workflowHostsSnapshot); // could be optimized, but for now, just create a copy and add the local node Set<InstanceNodeSessionId> tempWorkflowHostsAndSelf = new HashSet<InstanceNodeSessionId>(workflowHostsWorkingCopy); tempWorkflowHostsAndSelf.add(localNodeId); workflowHostsAndSelfSnapshot = Collections.unmodifiableSet(tempWorkflowHostsAndSelf); // FIXME >8.0 preliminary - this only supports the *default* logical node ids, not the ones published via other mechanisms logicalWorkflowHostsAndSelfSnapshot = convertFromInstanceIdsToLogicalNodesSet(workflowHostsAndSelfSnapshot); callbackManager.enqueueCallback(new AsyncCallback<WorkflowHostSetListener>() { @Override public void performCallback(WorkflowHostSetListener listener) { // FIXME implement difference sets or change listener interface listener.onReachableWorkflowHostsChanged(workflowHostsSnapshot, null, null); } }); } } private Set<LogicalNodeId> convertFromInstanceIdsToLogicalNodesSet(Set<InstanceNodeSessionId> input) { // FIXME >8.0 preliminary - this only supports the *default* logical node ids, not the ones published via other mechanisms final Set<LogicalNodeId> tempSet = new HashSet<LogicalNodeId>(); for (InstanceNodeSessionId instanceSessionId : input) { tempSet.add(instanceSessionId.convertToDefaultLogicalNodeId()); } return Collections.unmodifiableSet(tempSet); } }