From 86bac47fb720c27e5c1852833329c89e4caa2051 Mon Sep 17 00:00:00 2001 From: Ferdinando Villa Date: Tue, 12 Nov 2024 17:06:25 +0100 Subject: [PATCH] Activity/resolution/contextualization logics revised --- .../klab/api/data/KnowledgeGraph.java | 44 +++++- .../klab/api/services/Reasoner.java | 10 +- .../reasoner/ObservationReasoner.java | 2 +- .../services/reasoner/ReasonerService.java | 3 +- .../services/runtime/ExecutionSequence.java | 2 +- .../klab/services/runtime/RuntimeService.java | 19 ++- .../library/core/CoreLibraryFunctors.java | 137 ----------------- .../runtime/neo4j/AbstractKnowledgeGraph.java | 138 +++++++++++++++++- .../runtime/neo4j/KnowledgeGraphNeo4j.java | 4 +- 9 files changed, 201 insertions(+), 158 deletions(-) delete mode 100644 klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/library/core/CoreLibraryFunctors.java diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/data/KnowledgeGraph.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/data/KnowledgeGraph.java index 905d42b1c..ece440118 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/data/KnowledgeGraph.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/data/KnowledgeGraph.java @@ -2,6 +2,7 @@ import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; import org.integratedmodelling.klab.api.knowledge.observation.Observation; +import org.integratedmodelling.klab.api.provenance.Activity; import org.integratedmodelling.klab.api.provenance.Agent; import org.integratedmodelling.klab.api.scope.ContextScope; import org.integratedmodelling.klab.api.scope.Scope; @@ -9,6 +10,7 @@ import org.integratedmodelling.klab.api.services.runtime.objects.ContextInfo; import org.integratedmodelling.klab.api.services.runtime.objects.SessionInfo; +import java.io.Closeable; import java.net.URL; import java.util.List; @@ -31,8 +33,12 @@ public interface KnowledgeGraph { * Operations are defined and run to modify the knowledge graph. The operation API guarantees the proper * updating of provenance in the graph so that any modification is recorded, attributed and saves in * replayable history. + *

+ * At close, the operation commits or rolls back changes (except the activity it creates in provenance) + * according to which finalization mechanism has been called. If none has been called, the activity will + * be stored as an internal failure and everything else rolled back. */ - interface Operation { + interface Operation extends Closeable { /** * Any operation on the KG is done by someone or something, dutifully recorded in the provenance. @@ -41,6 +47,36 @@ interface Operation { */ Agent getAgent(); + /** + * This is only used to pass the activity to a child operation. + * + * @return + */ + Activity getActivity(); + + /** + * Store the passed asset, return its unique long ID. + * + * @param asset + * @param additionalProperties any pair of properties we want overridden. Pass pairs and do it right + * or you'll get an exception. + * @return + */ + long store(RuntimeAsset asset, Scope scope, Object... additionalProperties); + + /** + * Link the two passed assets. + * + * @param source + * @param destination + * @param additionalProperties any pair of properties we want overridden. Pass pairs and do it right + * or you'll get an exception. + */ + void link(RuntimeAsset source, RuntimeAsset destination, + DigitalTwin.Relationship relationship, Scope scope, + Object... additionalProperties); + + /** * Run the operation as configured and return the ID of the last object created or modified, or * {@link Observation#UNASSIGNED_ID} if the operation failed or was wrongly defined. @@ -56,6 +92,7 @@ interface Operation { * * @param observation * @return + * @deprecated */ Operation add(RuntimeAsset observation); @@ -66,6 +103,7 @@ interface Operation { * * @param source * @return + * @deprecated */ Operation set(RuntimeAsset source, Object... properties); @@ -82,6 +120,7 @@ interface Operation { * @param assetTo * @param linkData * @return + * @deprecated */ Operation link(RuntimeAsset assetFrom, RuntimeAsset assetTo, DigitalTwin.Relationship relationship, Object... linkData); @@ -113,7 +152,6 @@ Operation link(RuntimeAsset assetFrom, RuntimeAsset assetTo, DigitalTwin.Relatio * @return */ Operation fail(ContextScope scope, Object... assets); - } /** @@ -222,7 +260,7 @@ List get(RuntimeAsset source, DigitalTwin.Relationsh * * @param observation * @param scope - * @param arguments additional parameters to add to the observation or to override existing ones + * @param arguments additional parameters to add to the observation or to override existing ones */ void update(RuntimeAsset observation, ContextScope scope, Object... arguments); diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/Reasoner.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/Reasoner.java index 6623e7c1f..48bce8bac 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/Reasoner.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/Reasoner.java @@ -621,6 +621,8 @@ interface Capabilities extends ServiceCapabilities { Concept describedType(Semantics concept); /** + * Observational compatibility, considering all elements of the expression. + * * @param concept * @param other * @return @@ -1009,10 +1011,10 @@ interface Admin { /** * The "port" to ingest an individual concept definition, called by - * {@link #loadKnowledge(Worldview, Scope)} (Worldview)}. Provided separately to make it possible - * for a resolver service to declare individual local concepts, as long as it owns the semantic - * service. Definition must be made only in terms of known concepts (no forward declaration is - * allowed), so order of ingestion is critical. + * {@link #loadKnowledge(Worldview, Scope)} (Worldview)}. Provided separately to make it possible for + * a resolver service to declare individual local concepts, as long as it owns the semantic service. + * Definition must be made only in terms of known concepts (no forward declaration is allowed), so + * order of ingestion is critical. * * @param statement * @param scope admin user scope to report and validate diff --git a/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationReasoner.java b/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationReasoner.java index 339838ece..8ea5b634a 100644 --- a/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationReasoner.java +++ b/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationReasoner.java @@ -223,7 +223,7 @@ private boolean matchFilter(KimObservationStrategy.Filter filter, Observation ob if (filter.getMatch() != null) { var semantics = filter.getMatch().isPattern() ? reasoner.declareConcept(filter.getMatch(), patternVariableValues) : reasoner.declareConcept(filter.getMatch()); - ret = semantics != null && reasoner.compatible(observation.getObservable(), semantics); + ret = semantics != null && reasoner.match(observation.getObservable(), semantics); } if (ret && !filter.getFunctions().isEmpty()) { for (var function : filter.getFunctions()) { diff --git a/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ReasonerService.java b/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ReasonerService.java index 14128ecaf..990b468d3 100644 --- a/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ReasonerService.java +++ b/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ReasonerService.java @@ -1781,7 +1781,8 @@ public Collection created(Semantics semantics) { @Override public boolean match(Semantics candidate, Semantics pattern) { - return false; + // FIXME definitely not the same thing! + return compatible(candidate, pattern); } @Override diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/ExecutionSequence.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/ExecutionSequence.java index 439d4de2c..467153434 100644 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/ExecutionSequence.java +++ b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/ExecutionSequence.java @@ -100,7 +100,7 @@ public boolean run() { } } - /** + /* * Run also the empty operations because execution will update the observations */ if (scope.getParallelism() == Parallelism.ONE) { diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java index 873349b68..e4209d3cb 100644 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java +++ b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java @@ -16,6 +16,7 @@ import org.integratedmodelling.klab.api.knowledge.observation.impl.ObservationImpl; import org.integratedmodelling.klab.api.lang.Contextualizable; import org.integratedmodelling.klab.api.provenance.Activity; +import org.integratedmodelling.klab.api.provenance.impl.ActivityImpl; import org.integratedmodelling.klab.api.scope.ContextScope; import org.integratedmodelling.klab.api.scope.Scope; import org.integratedmodelling.klab.api.scope.SessionScope; @@ -269,6 +270,7 @@ public String registerContext(ContextScope contextScope) { @Override public long submit(Observation observation, ContextScope scope) { if (scope instanceof ServiceContextScope serviceContextScope) { + // TODO this gets its operation (instantiation of observation) return serviceContextScope.insertIntoKnowledgeGraph(observation); } return Observation.UNASSIGNED_ID; @@ -282,8 +284,12 @@ public Future resolve(long id, ContextScope scope) { var resolver = serviceContextScope.getService(Resolver.class); var observation = serviceContextScope.getObservation(id); var digitalTwin = getDigitalTwin(scope); - var activity = digitalTwin.knowledgeGraph().activity(digitalTwin.knowledgeGraph().klab(), scope, - observation, Activity.Type.RESOLUTION, null); + var activity = + + // TODO retrieve the activity that instantiated the observation instead, pass it down + // as the root for the resolution somehow + digitalTwin.knowledgeGraph().activity(digitalTwin.knowledgeGraph().klab(), scope, + observation, Activity.Type.RESOLUTION, null); final var ret = new CompletableFuture(); @@ -292,9 +298,11 @@ public Future resolve(long id, ContextScope scope) { var result = observation; scope.send(Message.MessageClass.ObservationLifecycle, Message.MessageType.ResolutionStarted, result); + // TODO resolution gets its own operation (find the instantiation activity as precursor) var dataflow = resolver.resolve(observation, scope); if (dataflow != null) { if (!dataflow.isEmpty()) { + // TODO contextualization gets its own operation (dependent on resolution) result = runDataflow(dataflow, scope); ret.complete(result); } @@ -322,6 +330,13 @@ public Future resolve(long id, ContextScope scope) { @Override public Observation runDataflow(Dataflow dataflow, ContextScope contextScope) { + var activity = new ActivityImpl(); + // TODO fill in the activity for an external dataflow run + return runDataflow(dataflow, contextScope, activity); + } + + public Observation runDataflow(Dataflow dataflow, ContextScope contextScope, + Activity activity) { var digitalTwin = getDigitalTwin(contextScope); diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/library/core/CoreLibraryFunctors.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/library/core/CoreLibraryFunctors.java deleted file mode 100644 index fda7ec7c0..000000000 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/library/core/CoreLibraryFunctors.java +++ /dev/null @@ -1,137 +0,0 @@ -//package org.integratedmodelling.klab.services.runtime.library.core; -// -//import io.micrometer.common.annotation.ValueResolver; -//import org.integratedmodelling.klab.api.Klab; -//import org.integratedmodelling.klab.api.data.mediation.Currency; -//import org.integratedmodelling.klab.api.data.mediation.NumericRange; -//import org.integratedmodelling.klab.api.data.mediation.Unit; -//import org.integratedmodelling.klab.api.knowledge.Artifact; -//import org.integratedmodelling.klab.api.lang.ServiceCall; -//import org.integratedmodelling.klab.api.services.runtime.extension.KlabFunction; -//import org.integratedmodelling.klab.api.services.runtime.extension.KlabFunction.Argument; -//import org.integratedmodelling.klab.api.services.runtime.extension.Library; -// -///** -// * Implementations for the core library functions. The language service will handle these and give access to -// * the functors through their {@link ServiceCall}s. Each service call must correspond to those built by the -// * {@link org.integratedmodelling.klab.api.services.CoreLibrary} service implemented across communicating -// * k.LAB services. -// *

-// * @deprecated the StandardLibrary.KlabCore enum should suffice, implemented in the -// * runtime and specifically translated by the executors and mappers. Each enum should contain the -// * scalar/vector nature and the needed pairs for dataflow validation. -// */ -//@Library(name = Klab.StandardLibrary.KlabCore.NAMESPACE, description = "") -//public class CoreLibraryFunctors { -// -//// @Library(name = "resources", description = "") -//// public static class Resources { -//// -//// // TODO -//// @KlabFunction(name = Klab.StandardLibrary.KlabCore.URN_RESOLVER, -//// dataflowLabel = -//// "Resource", description = "Contextualize a quality resource to obtain data", -//// parameters = { -//// @Argument(name = "urn", description = "The URN of the resource to " + -//// "contextualize", type = -//// Artifact.Type.TEXT)}, type = Artifact.Type.VALUE) -//// public static class ResourceResolver implements Resolver { -//// -//// /* -//// * TODO allow inserting a list of ValueResolver (or other functions) to perform mediations -//// * and/or classifications that are declared in sequence after the resource. -//// */ -//// -//// @Override -//// public void resolve(State observation, ServiceCall call, ContextScope scope) { -//// // TODO Auto-generated method stub -//// -//// /** -//// * TODO -//// * -//// * For multi-valued even grids in non-local resources, if we can split the scale into -//// * comparable contiguous sections and we find the resource in >1 services, we can -//// * retrieve tiles concurrently instead of downloading the entire monster. The storage -//// * can be tiled or not. This can be triggered after considering the size of the context. -//// */ -//// } -//// -//// } -// -// @KlabFunction(name = Klab.StandardLibrary.KlabCore.LUT_RESOLVER, -// dataflowLabel = -// "Lookup table", description = "Compute outputs by looking up dependency " + -// "values in a table", -// parameters = { -// @Argument(name = "urn", description = "The URN of table to use", type = -// Artifact.Type.TEXT)}, type = -// Artifact.Type.VALUE) -// public static class LookupTableResolver implements ValueResolver { -// -// @Override -// public String resolve(Object o) { -// return null; -// } -// } -// -//// @KlabFunction(name = Klab.StandardLibrary.KlabCore.URN_INSTANTIATOR, -//// dataflowLabel = "Resource", description = "Contextualize a Type.OBJECT resource to " + -//// "obtain " + -//// "objects", parameters = { -//// @Argument(name = "urn", description = "The URN of the resource to contextualize", type = -//// Artifact.Type.TEXT), -//// @Argument(name = "whole", description = "Cut the spatial extent of the resulting object " + -//// "instead of " + -//// "intersecting with the context shape", type = Artifact.Type.BOOLEAN)}, type = -//// Artifact.Type.OBJECT) -//// public static class SubjectResourceInstantiator implements Instantiator { -//// -//// @Override -//// public List resolve(Observable semantics, ServiceCall call, -//// ContextScope scope) { -//// -//// /** -//// * TODO -//// * -//// * Investigate if we can split the scale into comparable contiguous sections and we find -//// * the resource in >1 services to access the data concurrently in sections. There are -//// * issues with overlapping objects and decisions about the number of objects that would -//// * require previous knowledge (e.g. the "density" of objects in each tile). This would -//// * benefit from creating and using machine-learned metadata. -//// */ -//// -//// return null; -//// } -//// -//// } -//// } -// -// @Library(name = "values", description = "") -// public static class ValueOperators { -// // TODO value operator handlers - add POD type for non-boxing ones in declaration -// } -// -// @Library(name = "mediators", description = "") -// public static class Mediators { -// // TODO mediator methods -// -// public static double convertUnit(double original, Unit originalUnit, Unit targetUnit) { -// return 0; -// } -// -// public static double convertCurrency(double original, Currency originalUnit, Currency targetUnit) { -// return 0; -// } -// -// public static double convertRange(double original, NumericRange originalUnit, -// NumericRange targetUnit) { -// return 0; -// } -// } -// -// @Library(name = "dereifiers", description = "") -// public static class Dereifiers { -// // TODO mediator methods -// } -// -//} diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/AbstractKnowledgeGraph.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/AbstractKnowledgeGraph.java index d092f2e46..e5e90e23a 100644 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/AbstractKnowledgeGraph.java +++ b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/AbstractKnowledgeGraph.java @@ -1,6 +1,5 @@ package org.integratedmodelling.klab.services.runtime.neo4j; -import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -9,8 +8,8 @@ import org.integratedmodelling.klab.api.data.KnowledgeGraph; import org.integratedmodelling.klab.api.data.RuntimeAsset; import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; +import org.integratedmodelling.klab.api.engine.Engine; import org.integratedmodelling.klab.api.exceptions.KlabInternalErrorException; -import org.integratedmodelling.klab.api.exceptions.KlabUnimplementedException; import org.integratedmodelling.klab.api.knowledge.SemanticType; import org.integratedmodelling.klab.api.knowledge.observation.Observation; import org.integratedmodelling.klab.api.provenance.Activity; @@ -21,7 +20,9 @@ import org.integratedmodelling.klab.api.scope.Scope; import org.integratedmodelling.klab.api.services.Language; import org.integratedmodelling.klab.api.services.runtime.Actuator; +import org.neo4j.driver.Transaction; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -43,11 +44,116 @@ public RuntimeAsset load(Long key) throws Exception { } }); - public record Step(OperationImpl.Type type, List targets, Object[] parameters) { + public record Step(OperationImplObsolete.Type type, List targets, Object[] parameters) { } + /** + * A provenance-linked "transaction" that can be committed or rolled back by reporting failure or success. + * The related activity becomes part of the graph in any case and success/failure is recorded with it. + * Everything else stored or linked is rolled back in case of failure. + */ public class OperationImpl implements Operation { + ActivityImpl activity; + AgentImpl agent; + Transaction transaction; + Scope.Status outcome; + Throwable exception; + + /** + * What should be passed: an agent that will own the activity; the current context scope for graph + * operations; the activity type + *

+ * What can be passed: an activity as the parent of the one we create here; content for the activity + * such as description + * + * The store/link methods use the same on the database, under the transaction we opened. + * @param arguments + */ + public OperationImpl(Object... arguments) { + + // select arguments and put them where they belong + + // validate arguments and complain loudly if anything is missing + + // create and commit the activity record as a node, possibly linked to a parent + // activity. + + // open the transaction for the remaining operations + } + + + @Override + public Agent getAgent() { + return null; + } + + @Override + public Activity getActivity() { + return null; + } + + @Override + public long store(RuntimeAsset asset, Scope scope, Object... additionalProperties) { + return 0; + } + + @Override + public void link(RuntimeAsset source, RuntimeAsset destination, + DigitalTwin.Relationship relationship, Scope scope, Object... additionalProperties) { + + } + + @Override + public long run(ContextScope scope) { + return 0; + } + + @Override + public Operation add(RuntimeAsset observation) { + return null; + } + + @Override + public Operation set(RuntimeAsset source, Object... properties) { + // TODO remove + return null; + } + + @Override + public Operation link(RuntimeAsset assetFrom, RuntimeAsset assetTo, + DigitalTwin.Relationship relationship, Object... linkData) { + // TODO remove + return null; + } + + @Override + public Operation rootLink(RuntimeAsset asset, Object... linkData) { + // TODO remove + return null; + } + + @Override + public Operation success(ContextScope scope, Object... assets) { + // commit + return null; + } + + @Override + public Operation fail(ContextScope scope, Object... assets) { + // rollback + return null; + } + + @Override + public void close() throws IOException { + // TODO commit or rollback based on status after success() or fail(). If none has been + // called, status is null and this is an internal error, logged with the activity + } + } + + public class OperationImplObsolete implements Operation { + private AgentImpl agent; private String description; private ActivityImpl activity = new ActivityImpl(); @@ -80,6 +186,11 @@ public Object getAssetKey(RuntimeAsset asset) { throw new KlabInternalErrorException("Unregistered asset in graph operation: " + asset); } + @Override + public void close() throws IOException { + + } + enum Type { CREATE, MODIFY, @@ -163,6 +274,17 @@ public Agent getAgent() { return agent; } + @Override + public long store(RuntimeAsset asset, Scope scope, Object... additionalProperties) { + return 0; + } + + @Override + public void link(RuntimeAsset source, RuntimeAsset destination, + DigitalTwin.Relationship relationship, Scope scope, Object... additionalProperties) { + + } + public void setAgent(Agent agent) { this.agent = Agent.promote(agent); } @@ -265,7 +387,7 @@ protected abstract void link(RuntimeAsset source, RuntimeAsset destination, DigitalTwin.Relationship relationship, Scope scope, Object... additionalProperties); - protected abstract long runOperation(OperationImpl operation, ContextScope scope); + protected abstract long runOperation(OperationImplObsolete operation, ContextScope scope); /** * Call at the end of each activity on the result of {@link #activity(Agent, ContextScope, Object...)}, @@ -276,13 +398,14 @@ protected abstract void link(RuntimeAsset source, RuntimeAsset destination, * @param success * @param resultsToUpdate */ - protected abstract void finalizeOperation(OperationImpl operation, ContextScope scope, boolean success, + protected abstract void finalizeOperation(OperationImplObsolete operation, ContextScope scope, + boolean success, Object... resultsToUpdate); @Override public Operation activity(Agent agent, ContextScope scope, Object... targets) { - OperationImpl ret = new OperationImpl(); + OperationImplObsolete ret = new OperationImplObsolete(); ret.activity.setStart(System.currentTimeMillis()); @@ -368,7 +491,8 @@ protected Map asParameters(Object asset, Object... additionalPar switch (asset) { case Observation observation -> { ret.putAll(observation.getMetadata()); - ret.put("name", observation.getName() == null ? observation.getObservable().codeName() : observation.getName()); + ret.put("name", observation.getName() == null ? observation.getObservable().codeName() + : observation.getName()); ret.put("updated", observation.getLastUpdate()); ret.put("resolved", observation.isResolved()); ret.put("type", observation.getType().name()); diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4j.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4j.java index d19682776..f03bf2e82 100644 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4j.java +++ b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4j.java @@ -426,7 +426,7 @@ private void setId(RuntimeAsset asset, long id) { } @Override - protected long runOperation(OperationImpl operation, ContextScope scope) { + protected long runOperation(OperationImplObsolete operation, ContextScope scope) { operation.registerAsset(scope, "id", scope.getId()); @@ -720,7 +720,7 @@ private String getLabel(Object target) { } @Override - protected void finalizeOperation(OperationImpl operation, ContextScope scope, boolean success, + protected void finalizeOperation(OperationImplObsolete operation, ContextScope scope, boolean success, Object... results) { Dataflow dataflow = null;