/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.gui.datamanagement.browser;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.osgi.framework.Version;
import de.rcenvironment.core.authentication.AuthenticationException;
import de.rcenvironment.core.communication.api.PlatformService;
import de.rcenvironment.core.communication.common.CommunicationException;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.common.LogicalNodeId;
import de.rcenvironment.core.communication.common.ResolvableNodeId;
import de.rcenvironment.core.component.datamanagement.api.DefaultComponentHistoryDataItem;
import de.rcenvironment.core.component.datamanagement.history.HistoryMetaDataKeys;
import de.rcenvironment.core.datamanagement.MetaDataService;
import de.rcenvironment.core.datamanagement.commons.ComponentInstance;
import de.rcenvironment.core.datamanagement.commons.ComponentRun;
import de.rcenvironment.core.datamanagement.commons.EndpointData;
import de.rcenvironment.core.datamanagement.commons.MetaData;
import de.rcenvironment.core.datamanagement.commons.MetaDataKeys;
import de.rcenvironment.core.datamanagement.commons.MetaDataSet;
import de.rcenvironment.core.datamanagement.commons.WorkflowRun;
import de.rcenvironment.core.datamanagement.commons.WorkflowRunDescription;
import de.rcenvironment.core.datamodel.api.DataModelConstants;
import de.rcenvironment.core.datamodel.api.FinalComponentRunState;
import de.rcenvironment.core.datamodel.api.FinalWorkflowState;
import de.rcenvironment.core.datamodel.api.TypedDatum;
import de.rcenvironment.core.datamodel.api.TypedDatumSerializer;
import de.rcenvironment.core.datamodel.api.TypedDatumService;
import de.rcenvironment.core.datamodel.types.api.FileReferenceTD;
import de.rcenvironment.core.gui.datamanagement.browser.spi.CommonHistoryDataItemSubtreeBuilderUtils;
import de.rcenvironment.core.gui.datamanagement.browser.spi.ComponentHistoryDataItemSubtreeBuilder;
import de.rcenvironment.core.gui.datamanagement.browser.spi.DMBrowserNode;
import de.rcenvironment.core.gui.datamanagement.browser.spi.DMBrowserNodeConstants;
import de.rcenvironment.core.gui.datamanagement.browser.spi.DMBrowserNodeType;
import de.rcenvironment.core.gui.datamanagement.browser.spi.DMBrowserNodeUtils;
import de.rcenvironment.core.gui.resources.api.ImageManager;
import de.rcenvironment.core.gui.resources.api.StandardImages;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.common.VersionUtils;
import de.rcenvironment.core.utils.incubator.DebugSettings;
import de.rcenvironment.core.utils.incubator.ServiceRegistry;
import de.rcenvironment.core.utils.incubator.ServiceRegistryAccess;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncExceptionListener;
import de.rcenvironment.toolkit.modules.concurrency.api.CallablesGroup;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
/**
* @author Jan Flink
* @author Markus Litz
* @author Robert Mischke
* @author Christian Weiss
* @author Brigitte Boden
* @author Doreen Seider
*/
public class DMContentProvider implements ITreeContentProvider {
private static final String BRACKET_RIGHT = "]";
private static final String BRACKET_LEFT = " [";
private static final String FAILED = "FAILED";
private static final String VERIFICATION_FAILED = "RESULTS_REJECTED";
private static final String FINISHED = "FINISHED";
private static final int HUNDRET = 100;
private static final double FLOAT_ONE_HUNDRED = 100.0d;
private static final double FLOAT_KILO_BYTE = 1024.0;
private static final String NODE_TEXT_FORMAT_TITLE_PLUS_STATE = "%s --> %s";
private static final String NOT_TERMINATED_YET = "not terminated yet";
private static final String NOT_YET_AVAILABLE = "Not yet available";
private static final String UNKNOWN = "(unknown)";
private static final String REMOTE = "remote";
private static final String LOCAL = "local";
private static final String NO_BUILDER_ERROR_MESSAGE = "No subtree builder found for history data item with identifier: ";
private static final String STRING_SLASH = "/";
private static final String NODE_TEXT_FORMAT_TITLE_PLUS_HOSTNAME = "%s <%s>";
private static final String NODE_TEXT_FORMAT_TITLE_PLUS_TIMESTAMP_AND_HOST = "%s (%s) <%s>";
private static final String COMPONENT_NAME_AND_NODE_TEXT_FORMAT_TITLE_PLUS_TIMESTAMP_AND_HOST = "%s - %s (%s) <%s>";
private static final MetaData METADATA_COMPONENT_CONTEXT_ID = new MetaData(
MetaDataKeys.COMPONENT_CONTEXT_UUID, true, true);
private static final MetaData METADATA_WORKFLOW_FILES_DELETED = new MetaData(
MetaDataKeys.WORKFLOW_FILES_DELETED, true, true);
private static final MetaData METADATA_WORKFLOW_IS_MARKED_FOR_DELETION = new MetaData(
MetaDataKeys.WORKFLOW_MARKED_FOR_DELETION, true, true);
private static final MetaData METADATA_COMPONENT_CONTEXT_NAME = new MetaData(
MetaDataKeys.COMPONENT_CONTEXT_NAME, true, true);
private static final MetaData METADATA_INSTANCE_NODE_IDENTIFIER = new MetaData(
MetaDataKeys.NODE_IDENTIFIER, true, true);
private static final MetaData METADATA_COMPONENT_NAME = new MetaData(
MetaDataKeys.COMPONENT_NAME, true, true);
private static final MetaData METADATA_HISTORY_DATA_ITEM_IDENTIFIER = new MetaData(
HistoryMetaDataKeys.HISTORY_HISTORY_DATA_ITEM_IDENTIFIER, true, true);
private static final MetaData METADATA_HISTORY_USER_INFO_TEXT = new MetaData(
HistoryMetaDataKeys.HISTORY_USER_INFO_TEXT, true, true);
private static final MetaData METADATA_WORKFLOW_FINAL_STATE = new MetaData(MetaDataKeys.WORKFLOW_FINAL_STATE, true, true);
private static final MetaData METADATA_HISTORY_ORDERING = new MetaData(
HistoryMetaDataKeys.HISTORY_TIMESTAMP, true, true);
private static final String EXTENSION_POINT_ID_SUBTREE_BUILDER =
"de.rcenvironment.core.gui.datamanagement.browser.historysubtreebuilder";
protected final Log log = LogFactory.getLog(getClass());
private final boolean verboseLogging = DebugSettings.getVerboseLoggingEnabled(getClass());
private MetaDataService metaDataService;
private Map<String, ComponentHistoryDataItemSubtreeBuilder> historySubtreeBuilders;
/** Cached results for the MetaDataQuery. */
private Map<DMBrowserNode, WorkflowRun> workflowMetaDataMap = new HashMap<DMBrowserNode, WorkflowRun>();
/** Used to format timestamps in MetaData to readable dates. */
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private final List<DMBrowserNodeContentAvailabilityHandler> contentAvailabilityHandlers =
new CopyOnWriteArrayList<DMBrowserNodeContentAvailabilityHandler>();
private final Set<DMBrowserNode> inProgress = new CopyOnWriteArraySet<DMBrowserNode>();
private final Set<String> warningIsShown = new CopyOnWriteArraySet<String>();
private InstanceNodeSessionId localInstanceSessionId;
private TypedDatumSerializer typedDatumSerializer;
public DMContentProvider() throws AuthenticationException {
ServiceRegistryAccess serviceRegistryAccess = ServiceRegistry.createAccessFor(this);
metaDataService = serviceRegistryAccess.getService(MetaDataService.class);
typedDatumSerializer = serviceRegistryAccess.getService(TypedDatumService.class).getSerializer();
localInstanceSessionId = serviceRegistryAccess.getService(PlatformService.class).getLocalInstanceNodeSessionId();
registerBuilders();
}
private void registerBuilders() {
historySubtreeBuilders = new HashMap<String, ComponentHistoryDataItemSubtreeBuilder>();
// get all extensions
IConfigurationElement[] config = Platform.getExtensionRegistry()
.getConfigurationElementsFor(EXTENSION_POINT_ID_SUBTREE_BUILDER);
for (IConfigurationElement e : config) {
try {
final Object o = e.createExecutableExtension("class");
if (o instanceof ComponentHistoryDataItemSubtreeBuilder) {
ComponentHistoryDataItemSubtreeBuilder builder = (ComponentHistoryDataItemSubtreeBuilder) o;
for (String supported : builder.getSupportedHistoryDataItemIdentifier()) {
// do not allow ambiguous mappings
if (historySubtreeBuilders.containsKey(supported)) {
throw new IllegalStateException("More than one builder tried to register for key " + supported);
}
// register
historySubtreeBuilders.put(supported, builder);
}
if (log.isDebugEnabled()) {
log.debug("Registered subtree builder " + o.getClass());
}
}
} catch (CoreException ex) {
log.error("Error registering extension " + e, ex);
}
}
}
@Override
public DMBrowserNode[] getChildren(Object parent) {
final DMBrowserNode node = (DMBrowserNode) parent;
if (node.areChildrenKnown()) {
return node.getChildrenAsArray();
} else {
final Runnable retrieverTask = new RetrieverTask(node);
Job job = new Job(Messages.dataManagementBrowser) {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
monitor.beginTask(Messages.fetchingData, 3);
monitor.worked(2);
retrieverTask.run();
monitor.worked(1);
return Status.OK_STATUS;
} finally {
monitor.done();
}
};
};
job.setUser(true);
job.schedule();
// return a wait signal node as only child
final DMBrowserNode waitSignalNode = new DMBrowserNode(
Messages.waitSignalNodeLabel);
waitSignalNode.setType(DMBrowserNodeType.Loading);
waitSignalNode.markAsLeaf();
return new DMBrowserNode[] { waitSignalNode };
}
}
/**
* Fetching the children of {@link DMBrowserNode}.
*
* @param parent the parent {@link DMBrowserNode}
*/
public void fetchChildren(Object parent) {
try {
createChildrenForNode((DMBrowserNode) parent);
} catch (CommunicationException e) {
for (final DMBrowserNodeContentAvailabilityHandler handler : contentAvailabilityHandlers) {
handler.handleContentRetrievalError((DMBrowserNode) parent, e);
}
}
}
private void createChildrenForNode(final DMBrowserNode node) throws CommunicationException {
switch (node.getType()) {
case HistoryRoot:
createChildrenForHistoryRootNode(node);
break;
case Workflow:
createChildrenForWorkflowNode(node);
break;
case Timeline:
createChildrenForTimelineNode(node);
break;
case Components:
createChildrenForComponentsNode(node);
break;
case Component:
case HistoryObject:
break;
case DMDirectoryReference:
CommonHistoryDataItemSubtreeBuilderUtils.buildSubtreeForDirectoryItem(node.getDirectoryReferenceTD(), node, node.getParent());
break;
default:
log.warn("Unexpected node type: " + node.getType().name());
}
}
private WorkflowRun getMetaDataForWorkflow(DMBrowserNode workflowNode) throws CommunicationException {
// extract the id of the desired workflow
final Long workflowRunID = Long.valueOf(workflowNode.getMetaData().getValue(METADATA_COMPONENT_CONTEXT_ID));
final ResolvableNodeId workflowNodeId = workflowNode.getNodeIdentifier(); // TODO review: not DM node id?
synchronized (workflowMetaDataMap) {
if (workflowMetaDataMap.containsKey(workflowNode)) {
return workflowMetaDataMap.get(workflowNode);
}
}
final long start = System.currentTimeMillis();
WorkflowRun result = null;
try {
log.debug(StringUtils.format("Fetching run data of workflow #%s from %s", workflowRunID, workflowNodeId));
result = metaDataService.getWorkflowRun(workflowRunID, workflowNodeId);
log.debug(StringUtils.format("Finished fetching run data of workflow #%s from %s", workflowRunID, workflowNodeId));
} catch (CommunicationException e) {
// cache anyway to prevent repeated failing remote requests as this method is called multiple times when building the tree nodes
synchronized (workflowMetaDataMap) {
workflowMetaDataMap.put(workflowNode, null);
}
log.debug(StringUtils.format("Failed to fetch run data of workflow #%s from %s", workflowRunID, workflowNodeId));
throw e; // throw exception to keep logic for 6.3; could be improved though - seid_do
}
final long millis = System.currentTimeMillis() - start;
// TODO review: can "null" really happen here? (see catch block above) - misc_ro, Jul 2015
if (result == null) {
log.error(StringUtils.format("Unable to fetch meta data of workflow #%d from node %s", workflowRunID,
workflowNode.getName()));
} else {
if (verboseLogging) {
log.debug(StringUtils.format("Meta data query for workflow #%d (\'%s\') took %d ms", workflowRunID,
result.getWorkflowTitle(), millis));
}
}
// cache even in case of 'null' result to prevent repeated failing requests as this method is called multiple times when
// building the tree nodes
synchronized (workflowMetaDataMap) {
workflowMetaDataMap.put(workflowNode, result);
}
return result;
}
private void createChildrenForHistoryRootNode(DMBrowserNode parent) throws CommunicationException {
final long start = System.currentTimeMillis();
Set<WorkflowRunDescription> workflowDescriptions = metaDataService.getWorkflowRunDescriptions();
log.debug(StringUtils.format("query for all workflow run descriptions on all known nodes took %d ms",
(System.currentTimeMillis() - start)));
// map<id, node> to keep track of already-known contexts and their tree nodes
final Map<String, DMBrowserNode> encounteredContexts = new HashMap<>();
// map<instanceNode, map<id, node>> to keep track of already-known contexts and their tree nodes sorted by instance node id to allow
// parallelization later one
Map<String, Map<String, DMBrowserNode>> encounteredContextsByInstanceNode = new HashMap<>();
// map<id, node> to keep track of already-known contexts and their tree nodes
final Map<String, Long> workflowStarts = new HashMap<String, Long>();
/*
* Iterate over the results and for each new workflow create a node and register the subnodes as children.
*/
for (final WorkflowRunDescription wfd : workflowDescriptions) {
// extract the MetaDataSet
MetaDataSet mds = new MetaDataSet();
// extract the id of the workflow
String contextID = wfd.getWorkflowRunID().toString();
mds.setValue(METADATA_COMPONENT_CONTEXT_ID, contextID);
// extract the name of the workflow
String contextName = wfd.getWorkflowTitle();
mds.setValue(METADATA_COMPONENT_CONTEXT_NAME, contextName);
// try to extract the instance node id of the workflow
String instanceNodeId = wfd.getStorageLogicalNodeIdString();
mds.setValue(METADATA_INSTANCE_NODE_IDENTIFIER, instanceNodeId);
if (wfd.getFinalState() != null) {
mds.setValue(METADATA_WORKFLOW_FINAL_STATE, wfd.getFinalState().toString());
}
mds.setValue(METADATA_WORKFLOW_FILES_DELETED, wfd.getAreFilesDeleted().toString());
mds.setValue(METADATA_WORKFLOW_IS_MARKED_FOR_DELETION, wfd.isMarkedForDeletion().toString());
// if the workflow already has a node, this node is used to register the child nodes
DMBrowserNode contextDMObject = null;
// if the meta data for instance node is not set (as introduced in 5.2 first)
if (instanceNodeId == null) {
contextDMObject = encounteredContexts.get(contextID);
} else {
if (encounteredContexts.containsKey(instanceNodeId)) {
contextDMObject = encounteredContextsByInstanceNode.get(instanceNodeId).get(contextID);
}
}
// if it's a new workflow, a new workflow node is created as parent node
final String startTimeValue = wfd.getStartTime().toString();
// fix for mantis #6776: apply "virtual" timestamp value so sorting works again
mds.setValue(METADATA_HISTORY_ORDERING, startTimeValue);
final boolean startTimeSet = startTimeValue != null;
final Long workflowStart = workflowStarts.get(contextID);
final long startTime;
if (startTimeSet) {
startTime = Long.parseLong(startTimeValue);
} else {
startTime = 0;
}
if (contextDMObject == null) {
// create the workflow node and set its attributes
contextDMObject = new DMBrowserNode(contextName, parent);
contextDMObject.setName(contextName);
contextDMObject.setMetaData(mds);
contextDMObject.setType(DMBrowserNodeType.Workflow);
contextDMObject.setWorkflowID(contextID);
contextDMObject.setWorkflowControllerNode(wfd.getControllerLogicalNodeId());
// add workflow node to the child node set of the parent (root) node
parent.addChild(contextDMObject);
// register as known workflow
if (instanceNodeId == null) {
encounteredContexts.put(contextID, contextDMObject);
} else {
if (!encounteredContextsByInstanceNode.containsKey(instanceNodeId)) {
encounteredContextsByInstanceNode.put(instanceNodeId, new HashMap<String, DMBrowserNode>());
}
encounteredContextsByInstanceNode.get(instanceNodeId).put(contextID, contextDMObject);
}
// save workflow start timestamp
workflowStarts.put(contextID, startTime);
} else {
if (workflowStart > startTime) {
workflowStarts.put(contextID, startTime);
}
}
}
for (Map.Entry<String, DMBrowserNode> entry : encounteredContexts.entrySet()) {
setupWorkflowNode(entry.getValue());
}
// create parallel tasks
CallablesGroup<Void> callablesGroup = ConcurrencyUtils.getFactory().createCallablesGroup(Void.class);
for (String instanceNodeId : encounteredContextsByInstanceNode.keySet()) {
final Map<String, DMBrowserNode> encounteredContextsPerInstanceNode = encounteredContextsByInstanceNode.get(instanceNodeId);
callablesGroup.add(new Callable<Void>() {
@Override
@TaskDescription("Fetch data reference for workflow nodes by instance node id")
public Void call() throws Exception {
for (Map.Entry<String, DMBrowserNode> entry : encounteredContextsPerInstanceNode.entrySet()) {
setupWorkflowNode(entry.getValue());
}
return null;
}
});
}
callablesGroup.add(new Callable<Void>() {
@Override
@TaskDescription("Fetch data reference for workflow nodes")
public Void call() throws Exception {
for (Map.Entry<String, DMBrowserNode> entry : encounteredContexts.entrySet()) {
setupWorkflowNode(entry.getValue());
}
return null;
}
});
callablesGroup.executeParallel(new AsyncExceptionListener() {
@Override
public void onAsyncException(Exception e) {
if (e.getCause() == null) {
// log a compressed message; this includes the case of RemoteOperationExceptions, which (by design) never have a "cause"
log.warn("Asynchronous exception during parallel data reference query: " + e.toString());
} else {
// on unexpected errors, log the full stacktrace
log.warn("Asynchronous exception during parallel data reference query", e);
}
}
});
// sort nodes by start time
parent.sortChildren(DMBrowserNodeUtils.COMPARATOR_BY_HISTORY_TIMESTAMP_DESC);
}
private void setupWorkflowNode(DMBrowserNode workflowNode) {
if (isLocalWorkflow(workflowNode.getWorkflowControllerNode())) {
workflowNode.setWorkflowHostName(LOCAL);
} else {
workflowNode.setWorkflowHostName(REMOTE);
}
String wfNodeTitle = StringUtils.format(NODE_TEXT_FORMAT_TITLE_PLUS_HOSTNAME,
workflowNode.getName(), workflowNode.getWorkflowHostName());
String finalState = workflowNode.getMetaData().getValue(METADATA_WORKFLOW_FINAL_STATE);
if (finalState == null) {
wfNodeTitle = StringUtils.format(NODE_TEXT_FORMAT_TITLE_PLUS_STATE, wfNodeTitle,
NOT_TERMINATED_YET);
} else {
setWorkflowNodeIconFromFinalState(workflowNode, getFinalStateFromString(finalState));
}
workflowNode.setTitle(wfNodeTitle);
}
private boolean isLocalWorkflow(LogicalNodeId logicalNodeId) {
return localInstanceSessionId.isSameInstanceNodeAs(logicalNodeId);
}
private FinalWorkflowState getFinalStateFromString(String finalState) {
if (finalState != null) {
return FinalWorkflowState.valueOf(finalState);
}
// default
return null;
}
private void setWorkflowNodeIconFromFinalState(DMBrowserNode workflowNode, FinalWorkflowState finalState) {
if (finalState != null) {
// Select state icon for workflow
switch (finalState) {
case CANCELLED:
workflowNode.setIcon(ImageManager.getInstance().getSharedImage(StandardImages.CANCELLED));
break;
case FINISHED:
workflowNode.setIcon(ImageManager.getInstance().getSharedImage(StandardImages.FINISHED));
break;
case FAILED:
workflowNode.setIcon(ImageManager.getInstance().getSharedImage(StandardImages.FAILED));
break;
case RESULTS_REJECTED:
workflowNode.setIcon(ImageManager.getInstance().getSharedImage(StandardImages.RESULTS_REJECTED));
break;
case CORRUPTED:
workflowNode.setIcon(ImageManager.getInstance().getSharedImage(StandardImages.CORRUPTED));
break;
default:
break;
}
} else {
workflowNode.setIcon(null);
}
}
private void createChildrenForWorkflowNode(final DMBrowserNode workflowNode) throws CommunicationException {
final WorkflowRun workflowRun = getMetaDataForWorkflow(workflowNode);
if (workflowRun == null) {
workflowNode.setEnabled(false);
return;
}
if (workflowRun.getFinalState() != null) {
workflowNode.getMetaData().setValue(METADATA_WORKFLOW_FINAL_STATE, workflowRun.getFinalState().toString());
}
setupWorkflowNode(workflowNode);
addWorkflowErrorLogFileNode(workflowNode, workflowRun);
// create run information sub-tree
DMBrowserNode runInformation = new DMBrowserNode(Messages.runInformationTitle);
runInformation.setType(DMBrowserNodeType.WorkflowRunInformation);
FinalWorkflowState finalState = workflowRun.getFinalState();
Long starttime = workflowRun.getStartTime();
Long endtime = workflowRun.getEndTime();
Boolean areFilesDeleted = Boolean.valueOf(workflowNode.getMetaData().getValue(METADATA_WORKFLOW_FILES_DELETED));
if (workflowRun.getWfFileReference() != null) {
FileReferenceTD wfFileReference = (FileReferenceTD) typedDatumSerializer.deserialize(workflowRun.getWfFileReference());
DMBrowserNode wfFileNode = new DMBrowserNode(StringUtils.format(Messages.workflowFile, wfFileReference.getFileName()));
wfFileNode.setType(DMBrowserNodeType.DMFileResource);
wfFileNode.setDataReferenceId(wfFileReference.getFileReference());
wfFileNode.setAssociatedFilename(wfFileReference.getFileName());
wfFileNode.markAsLeaf();
runInformation.addChild(wfFileNode);
}
LogicalNodeId nodeId = workflowRun.getControllerLogicalNodeId();
if (nodeId != null) {
DMBrowserNode.addNewLeafNode(StringUtils.format(Messages.runInformationControllerNode, nodeId.getAssociatedDisplayName()),
DMBrowserNodeType.InformationText, runInformation);
}
// create component run information subtree
DMBrowserNode componentHostInformation = new DMBrowserNode(Messages.componentRunInformationSubtree);
componentHostInformation.setType(DMBrowserNodeType.ComponentHostInformation);
runInformation.addChild(componentHostInformation);
if (workflowRun.getComponentRuns().isEmpty()) {
DMBrowserNode.addNewLeafNode("Not (yet) available", DMBrowserNodeType.InformationText, componentHostInformation);
} else {
for (final ComponentInstance componentInstance : workflowRun.getComponentRuns().keySet()) {
if (workflowRun.getComponentRuns().get(componentInstance).size() > 0) {
final ComponentRun firstComponentRun = workflowRun.getComponentRuns().get(componentInstance).iterator().next();
final LogicalNodeId componentRunLogicalNodeId = firstComponentRun.getLogicalNodeId();
// TODO review: why/when can this be null? if it can be, this should be documented - misc_ro
if (componentRunLogicalNodeId != null) {
DMBrowserNode compNode = DMBrowserNode.addNewLeafNode(
StringUtils.format("%s: %s", componentInstance.getComponentInstanceName(),
componentRunLogicalNodeId.getAssociatedDisplayName()),
DMBrowserNodeType.Component, componentHostInformation);
MetaDataSet metaDataSet = new MetaDataSet();
final String componentName = componentInstance.getComponentInstanceName();
metaDataSet.setValue(METADATA_COMPONENT_NAME, componentName);
metaDataSet.setValue(METADATA_HISTORY_DATA_ITEM_IDENTIFIER, componentInstance.getComponentID()
.split(STRING_SLASH)[0]);
compNode.setMetaData(metaDataSet);
boolean failed = false;
boolean verificationFailed = false;
if (componentInstance.getFinalState() != null) {
failed = componentInstance.getFinalState().equals(FAILED);
verificationFailed = componentInstance.getFinalState().equals(VERIFICATION_FAILED);
}
setComponentIconForDMBrowserNode(compNode, failed, verificationFailed);
}
}
}
componentHostInformation.sortChildren(DMBrowserNodeUtils.COMPARATOR_BY_NODE_TITLE);
}
// create timeline sub-tree
if (starttime != null) {
DMBrowserNode.addNewLeafNode(StringUtils.format(Messages.runInformationStarttime, dateFormat.format(new Date(starttime))),
DMBrowserNodeType.InformationText, runInformation);
}
if (endtime != null) {
DMBrowserNode.addNewLeafNode(StringUtils.format(Messages.runInformationEndtime, dateFormat.format(new Date(endtime))),
DMBrowserNodeType.InformationText, runInformation);
} else {
if (finalState != null && finalState.equals(FinalWorkflowState.CORRUPTED)) {
DMBrowserNode.addNewLeafNode(StringUtils.format(Messages.runInformationEndtime, UNKNOWN),
DMBrowserNodeType.InformationText, runInformation);
} else {
DMBrowserNode.addNewLeafNode(StringUtils.format(Messages.runInformationEndtime, NOT_YET_AVAILABLE),
DMBrowserNodeType.InformationText, runInformation);
}
}
if (finalState != null) {
DMBrowserNode.addNewLeafNode(StringUtils.format(Messages.runInformationFinalState, finalState.getDisplayName()),
DMBrowserNodeType.InformationText,
runInformation);
} else {
DMBrowserNode.addNewLeafNode(StringUtils.format(Messages.runInformationFinalState, NOT_YET_AVAILABLE),
DMBrowserNodeType.InformationText,
runInformation);
}
if (areFilesDeleted) {
DMBrowserNode.addNewLeafNode(
StringUtils.format(Messages.additionalInformation, Messages.runInformationFilesDeleted),
DMBrowserNodeType.InformationText, runInformation);
}
if (workflowRun.getAdditionalInformationIfAvailable() != null) {
DMBrowserNode.addNewLeafNode(
StringUtils.format(Messages.runInformationAdditionalInformation, workflowRun.getAdditionalInformationIfAvailable()),
DMBrowserNodeType.InformationText, runInformation);
}
workflowNode.addChild(runInformation);
final DMBrowserNode timelineDMObject = new DMBrowserNode("Timeline");
timelineDMObject.setType(DMBrowserNodeType.Timeline);
workflowNode.addChild(timelineDMObject);
// create components sub-tree
final DMBrowserNode componentsNode = new DMBrowserNode("Timeline by Component");
componentsNode.setType(DMBrowserNodeType.Components);
workflowNode.addChild(componentsNode);
if (areFilesDeleted) {
setFileNodesEnabled(workflowNode, false);
}
}
private void createChildrenForTimelineNode(final DMBrowserNode timelineNode) throws CommunicationException {
final WorkflowRun workflowRun = getMetaDataForWorkflow(timelineNode.getNodeWithTypeWorkflow());
if (workflowRun == null) {
timelineNode.setEnabled(false);
return;
}
for (final ComponentInstance componentInstance : workflowRun.getComponentRuns().keySet()) {
for (final ComponentRun componentRun : workflowRun.getComponentRuns().get(componentInstance)) {
final String componentRunLocationTag;
if (wasComponentRunOnLocalInstance(componentRun)) {
componentRunLocationTag = LOCAL;
} else {
componentRunLocationTag = REMOTE;
}
MetaDataSet metaDataSet = new MetaDataSet();
final Long startTime = componentRun.getStartTime();
metaDataSet.setValue(METADATA_HISTORY_ORDERING, startTime.toString());
final String startDateString = dateFormat.format(new Date(startTime));
final String componentSpecificText = getNodeTitleForComponentRun(componentRun);
metaDataSet.setValue(METADATA_HISTORY_USER_INFO_TEXT, componentSpecificText);
final String componentName = componentInstance.getComponentInstanceName();
metaDataSet.setValue(METADATA_COMPONENT_NAME, componentName);
metaDataSet.setValue(METADATA_HISTORY_DATA_ITEM_IDENTIFIER, componentInstance.getComponentID().split(STRING_SLASH)[0]);
String componentRunNodeText = StringUtils.format(COMPONENT_NAME_AND_NODE_TEXT_FORMAT_TITLE_PLUS_TIMESTAMP_AND_HOST,
componentName, componentSpecificText, startDateString, componentRunLocationTag);
if (componentRun.getFinalState() != null && componentRun.getFinalState() != FinalComponentRunState.FINISHED
&& componentRun.getFinalState() != FinalComponentRunState.RESULTS_APPROVED) {
componentRunNodeText = componentRunNodeText.concat(BRACKET_LEFT + componentRun.getFinalState() + BRACKET_RIGHT);
}
DMBrowserNode dmoChild =
new DMBrowserNode(componentRunNodeText, timelineNode);
// dmoChild.setDataReferenceId(dataReferenceId);
dmoChild.setMetaData(metaDataSet);
dmoChild.setType(DMBrowserNodeType.HistoryObject);
boolean compRunFailed = false;
boolean resultsRejected = false;
if (componentRun.getFinalState() != null) {
compRunFailed = componentRun.getFinalState().equals(FinalComponentRunState.FAILED);
resultsRejected = componentRun.getFinalState().equals(FinalComponentRunState.RESULTS_REJECTED);
}
setComponentIconForDMBrowserNode(dmoChild, compRunFailed, resultsRejected);
createChildrenForHistoryObjectNode(dmoChild, componentRun);
// dmoChild.setAssociatedFilename(nodeName);
timelineNode.addChild(dmoChild);
addComponentLogFilesNode(dmoChild, componentRun);
}
}
// sort nodes by start time
timelineNode.sortChildren(DMBrowserNodeUtils.COMPARATOR_BY_HISTORY_TIMESTAMP_DESC);
setFileNodesEnabled(timelineNode,
!Boolean.valueOf(timelineNode.getNodeWithTypeWorkflow().getMetaData().getValue(METADATA_WORKFLOW_FILES_DELETED)));
}
private String getNodeTitleForComponentRun(final ComponentRun componentRun) {
String componentSpecificText;
if (componentRun.getRunCounter() == DataModelConstants.TEAR_DOWN_RUN) {
componentSpecificText = "Tear down";
} else if (componentRun.getRunCounter() == DataModelConstants.INIT_RUN) {
componentSpecificText = "Init";
} else {
componentSpecificText = StringUtils.format("Run %d", componentRun.getRunCounter());
}
return componentSpecificText;
}
private void createChildrenForComponentsNode(final DMBrowserNode componentsNode) throws CommunicationException {
final WorkflowRun workflowRun = getMetaDataForWorkflow(componentsNode.getNodeWithTypeWorkflow());
if (workflowRun == null) {
componentsNode.setEnabled(false);
return;
}
for (final ComponentInstance componentInstance : workflowRun.getComponentRuns().keySet()) {
MetaDataSet metaDataSet = new MetaDataSet();
final String componentName = componentInstance.getComponentInstanceName();
metaDataSet.setValue(METADATA_COMPONENT_NAME, componentName);
metaDataSet.setValue(METADATA_HISTORY_DATA_ITEM_IDENTIFIER, componentInstance.getComponentID().split(STRING_SLASH)[0]);
final String componentHostName;
if (workflowRun.getComponentRuns().get(componentInstance).size() > 0) {
final ComponentRun firstRun = workflowRun.getComponentRuns().get(componentInstance).iterator().next();
if (wasComponentRunOnLocalInstance(firstRun)) {
componentHostName = LOCAL;
} else {
componentHostName = REMOTE;
}
} else {
componentHostName = "";
}
DMBrowserNode componentNode = new DMBrowserNode("");
componentNode.setType(DMBrowserNodeType.Component);
componentNode.setMetaData(metaDataSet);
boolean failed = false;
boolean verificationFailed = false;
if (componentInstance.getFinalState() != null) {
failed = componentInstance.getFinalState().equals(FAILED);
verificationFailed = componentInstance.getFinalState().equals(VERIFICATION_FAILED);
}
setComponentIconForDMBrowserNode(componentNode, failed, verificationFailed);
boolean initIncluded = false;
boolean tearDownIncluded = false;
for (final ComponentRun componentRun : workflowRun.getComponentRuns().get(componentInstance)) {
final String componentRunLocationTag;
if (wasComponentRunOnLocalInstance(componentRun)) {
componentRunLocationTag = LOCAL;
} else {
componentRunLocationTag = REMOTE;
}
final Long startTime = componentRun.getStartTime();
MetaDataSet metaDataSetRun = new MetaDataSet();
metaDataSetRun.setValue(METADATA_COMPONENT_NAME, componentName);
metaDataSetRun.setValue(METADATA_HISTORY_DATA_ITEM_IDENTIFIER, componentInstance.getComponentID().split(STRING_SLASH)[0]);
metaDataSetRun.setValue(METADATA_HISTORY_ORDERING, startTime.toString());
final String startDateString = dateFormat.format(new Date(startTime));
final String componentSpecificText = getNodeTitleForComponentRun(componentRun);
metaDataSetRun.setValue(METADATA_HISTORY_USER_INFO_TEXT, componentSpecificText);
String componentRunNodeText = StringUtils.format(NODE_TEXT_FORMAT_TITLE_PLUS_TIMESTAMP_AND_HOST,
componentSpecificText, startDateString, componentRunLocationTag);
if (componentRun.getFinalState() != null && componentRun.getFinalState() != FinalComponentRunState.FINISHED
&& componentRun.getFinalState() != FinalComponentRunState.RESULTS_APPROVED) {
componentRunNodeText = componentRunNodeText.concat(BRACKET_LEFT + componentRun.getFinalState() + BRACKET_RIGHT);
}
DMBrowserNode dmoChild = new DMBrowserNode(componentRunNodeText, componentsNode);
dmoChild.setMetaData(metaDataSetRun);
dmoChild.setType(DMBrowserNodeType.HistoryObject);
boolean compRunFailed = false;
boolean resultsRejected = false;
if (componentRun.getFinalState() != null) {
compRunFailed = componentRun.getFinalState().equals(FinalComponentRunState.FAILED);
resultsRejected = componentRun.getFinalState().equals(FinalComponentRunState.RESULTS_REJECTED);
}
setComponentIconForDMBrowserNode(dmoChild, compRunFailed, resultsRejected);
createChildrenForHistoryObjectNode(dmoChild, componentRun);
componentNode.addChild(dmoChild);
addComponentLogFilesNode(dmoChild, componentRun);
if (componentRun.getRunCounter() == DataModelConstants.INIT_RUN) {
initIncluded = true;
} else if (componentRun.getRunCounter() == DataModelConstants.TEAR_DOWN_RUN) {
tearDownIncluded = true;
}
}
final String finalState = componentInstance.getFinalState();
String componentNodeText = StringUtils.format("%s (Runs: %d) <%s>", componentName,
getComponentRunCount(workflowRun, componentInstance, initIncluded, tearDownIncluded), componentHostName);
if (finalState != null && !finalState.equals(FINISHED)) {
componentNodeText = componentNodeText.concat(BRACKET_LEFT + finalState + BRACKET_RIGHT);
}
componentNode.setTitle(componentNodeText);
componentsNode.addChild(componentNode);
componentNode.sortChildren(DMBrowserNodeUtils.COMPARATOR_BY_HISTORY_TIMESTAMP_DESC);
}
// sort nodes by node title
componentsNode.sortChildren(DMBrowserNodeUtils.COMPARATOR_BY_NODE_TITLE);
setFileNodesEnabled(componentsNode,
!Boolean.valueOf(componentsNode.getNodeWithTypeWorkflow().getMetaData().getValue(METADATA_WORKFLOW_FILES_DELETED)));
}
private int getComponentRunCount(WorkflowRun workflowRun, ComponentInstance componentInstance, boolean initIncluded,
boolean tearDownIncluded) {
int runCount = workflowRun.getComponentRuns().get(componentInstance).size();
if (initIncluded) {
runCount--;
}
if (tearDownIncluded) {
runCount--;
}
return runCount;
}
/**
* Creates a browser node for the component log files.
*
* @param componentRun
*/
private void addComponentLogFilesNode(DMBrowserNode parentNode, ComponentRun componentRun) {
String logFileRef = componentRun.getLogFile();
String errorLogFileRef = componentRun.getErrorLogFile();
boolean logFilesAlwaysStored = VersionUtils.getVersionOfCoreBundles().compareTo(new Version("7.0.0")) >= 0;
if (logFilesAlwaysStored
|| (logFileRef != null || errorLogFileRef != null)) {
DMBrowserNode executionLogNode = null;
for (DMBrowserNode node : parentNode.getChildren()) {
if (node.getTitle().equals(DMBrowserNodeConstants.NODE_NAME_EXECUTION_LOG)) {
executionLogNode = node;
break;
}
}
if (executionLogNode != null) {
// in order to keep it the latest node for all component even if the have stored additional information and thus, created
// the node "Execution Log" beforehand
parentNode.removeChild(executionLogNode);
} else {
executionLogNode = new DMBrowserNode(DMBrowserNodeConstants.NODE_NAME_EXECUTION_LOG);
executionLogNode.setType(DMBrowserNodeType.LogFolder);
}
if (logFilesAlwaysStored || logFileRef != null) {
executionLogNode.addChild(createLogFileNode(logFileRef, false));
}
if (logFilesAlwaysStored || errorLogFileRef != null) {
executionLogNode.addChild(createLogFileNode(errorLogFileRef, true));
}
parentNode.addChild(executionLogNode);
// setFileNo/desEnabled(executionLogNode, !componentRun.isReferencesDeleted());
}
}
private void addWorkflowErrorLogFileNode(DMBrowserNode parentNode, WorkflowRun workflowRun) {
boolean logFilesAlwaysStored = VersionUtils.getVersionOfCoreBundles().compareTo(new Version("7.0.0")) >= 0;
String errorLogRef = workflowRun.getErrorLogFileReference();
if (logFilesAlwaysStored || errorLogRef != null) {
DMBrowserNode errorLogNode = createLogFileNode(errorLogRef, true);
parentNode.addChild(errorLogNode);
}
}
/**
* Creates a browser node for a log file.
*/
private DMBrowserNode createLogFileNode(String logFileRef, boolean errorLog) {
DMBrowserNode logFileNode;
if (logFileRef != null) {
FileReferenceTD logFileReference = (FileReferenceTD) typedDatumSerializer.deserialize(logFileRef);
logFileNode = new DMBrowserNode(getTitleForLogFileDMBrowserNode(logFileReference));
logFileNode.setType(DMBrowserNodeType.DMFileResource);
logFileNode.setDataReferenceId(logFileReference.getFileReference());
logFileNode.setAssociatedFilename(logFileReference.getFileName());
} else {
if (errorLog) {
logFileNode = new DMBrowserNode("[no error log]");
} else {
logFileNode = new DMBrowserNode("[no log]");
}
logFileNode.setType(DMBrowserNodeType.Custom);
logFileNode.setIcon(ImageManager.getInstance().getSharedImage(StandardImages.DATATYPE_FILE_16));
logFileNode.setEnabled(false);
}
logFileNode.markAsLeaf();
return logFileNode;
}
private String getTitleForLogFileDMBrowserNode(FileReferenceTD logFileReference) {
double fileSizeInKB = logFileReference.getFileSizeInBytes() / FLOAT_KILO_BYTE;
String nodeTitle;
if (fileSizeInKB == 0) {
nodeTitle = StringUtils.format("%s [0 KB]", logFileReference.getFileName());
} else if (fileSizeInKB < 1) {
nodeTitle = StringUtils.format("%s [%.2f KB]", logFileReference.getFileName(),
Math.round(fileSizeInKB * HUNDRET) / FLOAT_ONE_HUNDRED);
} else {
nodeTitle = StringUtils.format("%s [%d KB]", logFileReference.getFileName(), Math.round(fileSizeInKB));
}
return nodeTitle;
}
private void setComponentIconForDMBrowserNode(DMBrowserNode node, boolean isFailed, boolean isVerificationFailed) {
String identifier = node.getMetaData().getValue(METADATA_HISTORY_DATA_ITEM_IDENTIFIER);
ComponentHistoryDataItemSubtreeBuilder builder = getComponentHistoryDataItemSubtreeBuilder(node);
if (builder != null) {
Image componentIcon = builder.getComponentIcon(identifier);
Image decoratedImage = componentIcon;
// If component is failed, add small failed icon
ImageDescriptor overlayIconDescriptor = null;
if (isFailed) {
overlayIconDescriptor = DMBrowserImages.FAILED_SMALL;
if (overlayIconDescriptor != null) {
DecorationOverlayIcon decorationOverlayIcon =
new DecorationOverlayIcon(componentIcon, overlayIconDescriptor, IDecoration.TOP_RIGHT);
decoratedImage = decorationOverlayIcon.createImage();
}
} else if (isVerificationFailed) {
overlayIconDescriptor = DMBrowserImages.VERIFICATION_FAILED_SMALL;
if (overlayIconDescriptor != null) {
DecorationOverlayIcon decorationOverlayIcon =
new DecorationOverlayIcon(componentIcon, overlayIconDescriptor, IDecoration.TOP_RIGHT);
decoratedImage = decorationOverlayIcon.createImage();
}
}
node.setIcon(decoratedImage);
} else {
log.warn(NO_BUILDER_ERROR_MESSAGE + identifier);
}
}
private void createChildrenForHistoryObjectNode(final DMBrowserNode node, ComponentRun componentRun) {
ComponentHistoryDataItemSubtreeBuilder builder = getComponentHistoryDataItemSubtreeBuilder(node);
final LogicalNodeId componentRunNodeId = componentRun.getLogicalNodeId();
// TODO review: why/when can this be null? if it can be, this should be documented - misc_ro
if (componentRunNodeId != null) {
DMBrowserNode.addNewLeafNode(
StringUtils.format(Messages.componentRunInformationNode, componentRunNodeId.getAssociatedDisplayName()),
DMBrowserNodeType.InformationText, node);
}
String historyDataItem = componentRun.getHistoryDataItem();
if (historyDataItem != null) {
if (builder == null) {
String identifier = node.getMetaData().getValue(METADATA_HISTORY_DATA_ITEM_IDENTIFIER);
final String message = NO_BUILDER_ERROR_MESSAGE + identifier;
// TODO add warning as a tree node for better visibility?
log.warn(message);
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
// for some reason createChildrenForHistoryObjectNode is called twice on error -> prohibit that the error dialog is
// shown twice -- seid_do, April 2014
synchronized (warningIsShown) {
if (warningIsShown.contains(node.getPath())) {
return;
} else {
warningIsShown.add(node.getPath());
}
}
MessageDialog.openWarning(Display.getDefault().getActiveShell(), Messages.historyNodeWarningTitle,
StringUtils.format(Messages.historyNodeWarningMessage, node.getTitle()));
synchronized (warningIsShown) {
warningIsShown.remove(node.getPath());
}
}
});
} else {
builder.buildInitialHistoryDataItemSubtree(historyDataItem, node);
}
}
DefaultComponentHistoryDataItem defaultHistoryDataItem = new DefaultComponentHistoryDataItem();
if (componentRun.getEndpointData() != null && !componentRun.getEndpointData().isEmpty()) {
List<EndpointData> ed = new ArrayList<EndpointData>();
ed.addAll(componentRun.getEndpointData());
Collections.sort(ed);
for (EndpointData endpointData : ed) {
Map<String, String> metaData = endpointData.getEndpointInstance().getMetaData();
TypedDatum td = typedDatumSerializer.deserialize(endpointData.getDatum());
switch (endpointData.getEndpointInstance().getEndpointType()) {
case INPUT:
defaultHistoryDataItem.addInput(endpointData.getEndpointInstance().getEndpointName(), td);
defaultHistoryDataItem.setInputMetaData(endpointData.getEndpointInstance().getEndpointName(), metaData);
break;
case OUTPUT:
defaultHistoryDataItem.addOutput(endpointData.getEndpointInstance().getEndpointName(), td);
defaultHistoryDataItem.setOutputMetaData(endpointData.getEndpointInstance().getEndpointName(), metaData);
break;
default:
break;
}
}
CommonHistoryDataItemSubtreeBuilderUtils.buildDefaultHistoryDataItemSubtrees(defaultHistoryDataItem, node);
}
}
private void setFileNodesEnabled(DMBrowserNode node, boolean enable) {
if (!node.isLeafNode() && node.areChildrenKnown()) {
for (DMBrowserNode childNode : node.getChildren()) {
setFileNodesEnabled(childNode, enable);
}
} else if (node.getType().equals(DMBrowserNodeType.DMFileResource)
|| node.getType().equals(DMBrowserNodeType.DMDirectoryReference)) {
node.setEnabled(enable);
}
}
@Override
public DMBrowserNode[] getElements(Object inputElement) {
final DMBrowserNode[] result = getChildren(inputElement);
return result;
}
@Override
public Object getParent(Object element) {
DMBrowserNode dmo = (DMBrowserNode) element;
if (element == null) {
return null;
}
return dmo.getParent();
}
@Override
public boolean hasChildren(Object parent) {
if (parent instanceof DMBrowserNode) {
DMBrowserNode dmo = (DMBrowserNode) parent;
if (!dmo.areChildrenKnown()) {
// children unknown -> report "yes" to allow unfolding
return true;
} else {
// children known -> report "yes" if children list not empty
return dmo.getNumChildren() != 0;
}
} else {
return true;
}
}
protected boolean deleteWorkflowRun(DMBrowserNode browserNode) {
try {
metaDataService.deleteWorkflowRun(Long.valueOf(browserNode.getWorkflowID()), browserNode.getWorkflowControllerNode());
return true;
} catch (CommunicationException e) {
log.error("Could not delete workflow run in the database.", e);
}
return false;
}
protected void deleteWorkflowRunFiles(DMBrowserNode browserNode) {
try {
metaDataService.deleteWorkflowRunFiles(Long.valueOf(browserNode.getWorkflowID()), browserNode.getWorkflowControllerNode());
browserNode.getMetaData().setValue(METADATA_WORKFLOW_FILES_DELETED, String.valueOf(true));
} catch (CommunicationException e) {
log.error("Could not delete workflow run files in the database.", e);
}
}
/**
* Clear cached meta data.
*/
public void clear() {
synchronized (workflowMetaDataMap) {
workflowMetaDataMap.clear();
}
}
/**
* Clear cached meta data of a specific node.
*
* @param node The node to clear meta data of.
*/
public void clear(DMBrowserNode node) {
DMBrowserNode wfNode = node.getNodeWithTypeWorkflow();
if (wfNode != null) {
synchronized (workflowMetaDataMap) {
workflowMetaDataMap.remove(node);
}
}
}
@Override
public void dispose() {
synchronized (workflowMetaDataMap) {
workflowMetaDataMap.clear();
}
}
@Override
public void inputChanged(Viewer arg0, Object arg1, Object arg2) {}
/**
* Adds a {@link DMBrowserNodeContentAvailabilityHandler}.
*
* @param contentAvailabilityHandler the {@link DMBrowserNodeContentAvailabilityHandler}
*/
public void addContentAvailabilityHandler(
final DMBrowserNodeContentAvailabilityHandler contentAvailabilityHandler) {
contentAvailabilityHandlers.add(contentAvailabilityHandler);
}
/**
* Removes a {@link DMBrowserNodeContentAvailabilityHandler}.
*
* @param contentAvailabilityHandler the {@link DMBrowserNodeContentAvailabilityHandler}
*/
public void removeContentAvailabilityHandler(
final DMBrowserNodeContentAvailabilityHandler contentAvailabilityHandler) {
contentAvailabilityHandlers.remove(contentAvailabilityHandler);
}
/**
* {@link Runnable} realizing the logic for retrieving the content (childs) of a {@link DMBrowserNode}.
*
* @author Christian Weiss
*
*/
private class RetrieverTask implements Runnable {
private final DMBrowserNode node;
RetrieverTask(final DMBrowserNode node) {
this.node = node;
}
@Override
public void run() {
// avoid duplicate synchronous retrievals
synchronized (inProgress) {
if (inProgress.contains(node)) {
return;
}
inProgress.add(node);
}
try {
createChildrenForNode(node);
if (Thread.currentThread().isInterrupted()) {
return;
}
for (final DMBrowserNodeContentAvailabilityHandler handler : contentAvailabilityHandlers) {
handler.handleContentAvailable(node);
}
} catch (RuntimeException e) {
for (final DMBrowserNodeContentAvailabilityHandler handler : contentAvailabilityHandlers) {
handler.handleContentRetrievalError(node, e);
}
} catch (CommunicationException e) {
for (final DMBrowserNodeContentAvailabilityHandler handler : contentAvailabilityHandlers) {
handler.handleContentRetrievalError(node, e);
}
} finally {
synchronized (inProgress) {
inProgress.remove(node);
}
}
}
}
private ComponentHistoryDataItemSubtreeBuilder getComponentHistoryDataItemSubtreeBuilder(DMBrowserNode node) {
String identifier = node.getMetaData().getValue(METADATA_HISTORY_DATA_ITEM_IDENTIFIER);
for (String supportedIdentifier : historySubtreeBuilders.keySet()) {
if (identifier.matches(supportedIdentifier)) {
return historySubtreeBuilders.get(supportedIdentifier);
}
}
return null;
}
private boolean wasComponentRunOnLocalInstance(final ComponentRun firstRun) {
return localInstanceSessionId.isSameInstanceNodeAs(firstRun.getLogicalNodeId());
}
}