/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.datamanagement.internal;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
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.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.communication.management.WorkflowHostService;
import de.rcenvironment.core.datamanagement.DataManagementService;
import de.rcenvironment.core.datamanagement.MetaDataService;
import de.rcenvironment.core.datamanagement.RemotableMetaDataService;
import de.rcenvironment.core.datamanagement.commons.WorkflowRun;
import de.rcenvironment.core.datamanagement.commons.WorkflowRunDescription;
import de.rcenvironment.core.datamanagement.commons.WorkflowRunTimline;
import de.rcenvironment.core.datamodel.api.FinalComponentRunState;
import de.rcenvironment.core.datamodel.api.FinalComponentState;
import de.rcenvironment.core.datamodel.api.TimelineIntervalType;
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.toolkit.modules.concurrency.api.AsyncExceptionListener;
import de.rcenvironment.toolkit.modules.concurrency.api.CallablesGroup;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
/**
* Implementation of {@link DataManagementService}.
*
* @author Doreen Seider
* @author Jan Flink
* @author Robert Mischke
*/
public class MetaDataServiceImpl implements MetaDataService {
private CommunicationService communicationService;
private WorkflowHostService workflowHostService;
private final Log log = LogFactory.getLog(getClass());
@Override
public Long addComponentRun(Long componentInstanceId, String nodeId, Integer count, Long starttime,
ResolvableNodeId storageNodeId) throws CommunicationException {
try {
return getRemoteMetaDataService(storageNodeId).addComponentRun(componentInstanceId, nodeId, count, starttime);
} catch (RemoteOperationException e) {
throw new CommunicationException(
StringUtils.format("Failed to add component run from remote node @%s: ", storageNodeId) + e.getMessage());
}
}
@Override
public void addInputDatum(Long componentRunId, Long typedDatumId, Long endpointInstanceId, Integer count,
ResolvableNodeId storageNodeId) throws CommunicationException {
try {
getRemoteMetaDataService(storageNodeId).addInputDatum(componentRunId, typedDatumId, endpointInstanceId, count);
} catch (RemoteOperationException e) {
throw new CommunicationException(
StringUtils.format("Failed to add input datum from remote node @%s: ", storageNodeId) + e.getMessage());
}
}
@Override
public Long addOutputDatum(Long componentRunId, Long endpointInstanceId, String datum, Integer count,
ResolvableNodeId storageNodeId) throws CommunicationException {
try {
return getRemoteMetaDataService(storageNodeId).addOutputDatum(componentRunId, endpointInstanceId, datum, count);
} catch (RemoteOperationException e) {
throw new CommunicationException(
StringUtils.format("Failed to add output datum from remote node @%s: ", storageNodeId) + e.getMessage());
}
}
@Override
public void addComponentRunProperties(Long componentRunId, Map<String, String> properties, ResolvableNodeId storageNodeId)
throws CommunicationException {
try {
getRemoteMetaDataService(storageNodeId).addComponentRunProperties(componentRunId, properties);
} catch (RemoteOperationException e) {
throw new CommunicationException(StringUtils.format("Failed to add component run properties from remote node @%s: ",
storageNodeId) + e.getMessage());
}
}
@Override
public Long addTimelineInterval(Long workflowRunId, TimelineIntervalType intervalType, long starttime, Long relatedComponentId,
ResolvableNodeId storageNodeId) throws CommunicationException {
try {
return getRemoteMetaDataService(storageNodeId).addTimelineInterval(workflowRunId, intervalType, starttime, relatedComponentId);
} catch (RemoteOperationException e) {
throw new CommunicationException(
StringUtils.format("Failed to add timeline interval from remote node @%s: ", storageNodeId) + e.getMessage());
}
}
@Override
public void setTimelineIntervalFinished(Long timelineIntervalId, long endtime, ResolvableNodeId storageNodeId)
throws CommunicationException {
try {
getRemoteMetaDataService(storageNodeId).setTimelineIntervalFinished(timelineIntervalId, endtime);
} catch (RemoteOperationException e) {
throw new CommunicationException(StringUtils.format("Failed to set endtime of timeline interval from remote node @%s: ",
storageNodeId) + e.getMessage());
}
}
@Override
public void setOrUpdateHistoryDataItem(Long componentRunId, String historyDataItem, ResolvableNodeId storageNodeId)
throws CommunicationException {
try {
getRemoteMetaDataService(storageNodeId).setOrUpdateHistoryDataItem(componentRunId, historyDataItem);
} catch (RemoteOperationException e) {
throw new CommunicationException(StringUtils.format("Failed to update history data item from remote node @%s: ",
storageNodeId) + e.getMessage());
}
}
@Override
public void setComponentRunFinished(Long componentRunId, Long endtime, FinalComponentRunState finalState,
ResolvableNodeId storageNodeId)
throws CommunicationException {
try {
getRemoteMetaDataService(storageNodeId).setComponentRunFinished(componentRunId, endtime, finalState);
} catch (RemoteOperationException e) {
throw new CommunicationException(StringUtils.format("Failed to set endtime of component run from remote node @%s: ",
storageNodeId) + e.getMessage());
}
}
@Override
public void setComponentInstanceFinalState(Long componentInstanceId, FinalComponentState finalState, ResolvableNodeId storageNodeId)
throws CommunicationException {
try {
getRemoteMetaDataService(storageNodeId).setComponentInstanceFinalState(componentInstanceId, finalState);
} catch (RemoteOperationException e) {
throw new CommunicationException(StringUtils.format(
"Failed to set final state of component instance from remote node @%s: ", storageNodeId) + e.getMessage());
}
}
@Override
public Set<WorkflowRunDescription> getWorkflowRunDescriptions() throws CommunicationException {
Set<WorkflowRunDescription> descriptions = new HashSet<WorkflowRunDescription>();
CallablesGroup<Set> callablesGroup = ConcurrencyUtils.getFactory().createCallablesGroup(Set.class);
for (final InstanceNodeSessionId remoteInstanceSessionId : workflowHostService.getWorkflowHostNodesAndSelf()) {
callablesGroup.add(new Callable<Set>() {
@Override
@TaskDescription("Distributed query: getWorkflowDescriptions()")
public Set<WorkflowRunDescription> call() throws Exception {
Set<WorkflowRunDescription> workflowRunDescriptions =
getRemoteMetaDataService(remoteInstanceSessionId).getWorkflowRunDescriptions();
workflowRunDescriptions = fixInconsistentControllerNodeIds(remoteInstanceSessionId, workflowRunDescriptions);
return workflowRunDescriptions;
}
private Set<WorkflowRunDescription> fixInconsistentControllerNodeIds(final InstanceNodeSessionId remoteNodeId,
Set<WorkflowRunDescription> workflowRunDescriptions) {
boolean hasInconsistenControllerNodeIds = false;
for (WorkflowRunDescription wrd : workflowRunDescriptions) {
final LogicalNodeId controllerNodeId = wrd.getControllerLogicalNodeId();
final LogicalNodeId dmNodeId = wrd.getStorageLogicalNodeId();
if (!remoteInstanceSessionId.isSameInstanceNodeAs(controllerNodeId)
|| !remoteInstanceSessionId.isSameInstanceNodeAs(dmNodeId)) {
hasInconsistenControllerNodeIds = true;
}
}
if (hasInconsistenControllerNodeIds) {
// found at least one inconsistent workflow run, so rewrite the collection
Set<WorkflowRunDescription> newSet = new HashSet<>();
for (WorkflowRunDescription wrd : workflowRunDescriptions) {
final LogicalNodeId controllerNodeId = wrd.getControllerLogicalNodeId();
final LogicalNodeId dmNodeId = wrd.getStorageLogicalNodeId();
if (!remoteInstanceSessionId.isSameInstanceNodeAs(controllerNodeId)
|| !remoteInstanceSessionId.isSameInstanceNodeAs(dmNodeId)) {
log.warn(StringUtils.format(
"Replacing an inconsistent controller and/or storage node id (%s, %s) in workflow run #%d received "
+ "from node %s - most likely, the remote node's id has changed since the workflow was run",
controllerNodeId, dmNodeId, wrd.getWorkflowRunID(), remoteNodeId));
// TODO this should be the remote node's *logical node* id once migration is complete - misc_ro
newSet.add(WorkflowRunDescription.cloneAndReplaceNodeIds(wrd,
remoteInstanceSessionId.getInstanceNodeIdString()));
} else {
newSet.add(wrd);
}
}
workflowRunDescriptions = newSet;
}
return workflowRunDescriptions;
}
});
}
List<Set> results = callablesGroup.executeParallel(new AsyncExceptionListener() {
@Override
public void onAsyncException(Exception e) {
// log a compact message, no stacktrace
log.warn("Failed to query a node for workflow data management information: " + e.toString());
}
});
for (Collection<WorkflowRunDescription> singleResult : results) {
if (singleResult != null) {
descriptions.addAll(singleResult);
}
}
return descriptions;
}
@Override
public WorkflowRun getWorkflowRun(Long workflowRunId, ResolvableNodeId storageNodeId) throws CommunicationException {
try {
return getRemoteMetaDataService(storageNodeId).getWorkflowRun(workflowRunId);
} catch (RemoteOperationException e) {
throw new CommunicationException(
StringUtils.format("Failed to get workflow run from remote node @%s: ", storageNodeId) + e.getMessage());
}
}
@Override
public WorkflowRunTimline getWorkflowTimeline(Long workflowRunId, ResolvableNodeId storageNodeId) throws CommunicationException {
try {
return getRemoteMetaDataService(storageNodeId).getWorkflowTimeline(workflowRunId);
} catch (RemoteOperationException e) {
throw new CommunicationException(
StringUtils.format("Failed to get workflow timeline from remote node @%s: ", storageNodeId) + e.getMessage());
}
}
@Override
public Boolean deleteWorkflowRun(Long workflowRunId, ResolvableNodeId storageNodeId) throws CommunicationException {
try {
return getRemoteMetaDataService(storageNodeId).deleteWorkflowRun(workflowRunId);
} catch (RemoteOperationException e) {
throw new CommunicationException(
StringUtils.format("Failed to delete worklfow run from remote node @%s: ", storageNodeId) + e.getMessage());
}
}
@Override
public Boolean deleteWorkflowRunFiles(Long workflowRunId, ResolvableNodeId storageNodeId)
throws CommunicationException {
try {
return getRemoteMetaDataService(storageNodeId).deleteWorkflowRunFiles(workflowRunId);
} catch (RemoteOperationException e) {
throw new CommunicationException(StringUtils.format("Failed to delete files of worklfow run from remote node @%s: ",
storageNodeId) + e.getMessage());
}
}
protected void activate(BundleContext bundleContext) {}
protected void bindCommunicationService(CommunicationService newCommunicationService) {
communicationService = newCommunicationService;
}
protected void bindWorkflowHostService(WorkflowHostService newWorkflowHostService) {
workflowHostService = newWorkflowHostService;
}
private RemotableMetaDataService getRemoteMetaDataService(ResolvableNodeId nodeId) throws RemoteOperationException {
return (RemotableMetaDataService) communicationService.getRemotableService(RemotableMetaDataService.class, nodeId);
}
}