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 fb40cf6c6..276a4e95b 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 @@ -66,16 +66,14 @@ interface Operation extends Closeable { /** * Link the two passed assets. - *

- * FIXME remove scope when the other is gone - * + *

* * @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, + DigitalTwin.Relationship relationship, Object... additionalProperties); // diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/ResolutionConstraint.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/ResolutionConstraint.java index adef53235..ad6869698 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/ResolutionConstraint.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/ResolutionConstraint.java @@ -112,7 +112,7 @@ private Type(Class dataClass, boolean incremental) { */ ResolutionConstraint merge(ResolutionConstraint constraint); - static ResolutionConstraint of(Type type, Object data) { + static ResolutionConstraint of(Type type, Object... data) { return new ResolutionConstraintImpl(type, data); } diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/objects/ResolutionConstraintImpl.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/objects/ResolutionConstraintImpl.java index 4fbbdeda2..3f662928d 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/objects/ResolutionConstraintImpl.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/objects/ResolutionConstraintImpl.java @@ -24,7 +24,9 @@ public ResolutionConstraintImpl(Type type, Object... data) { throw new KlabIllegalArgumentException("Cannot create resolution constraint: illegal data " + "content"); } - this.data.add(o); + if (o != null) { + this.data.add(o); + } } } diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/data/jackson/JacksonConfiguration.java b/klab.core.common/src/main/java/org/integratedmodelling/common/data/jackson/JacksonConfiguration.java index e2ab5d460..66e721136 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/data/jackson/JacksonConfiguration.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/data/jackson/JacksonConfiguration.java @@ -32,6 +32,9 @@ import org.integratedmodelling.klab.api.lang.kactors.KActorsBehavior; import org.integratedmodelling.klab.api.lang.kdl.KdlDataflow; import org.integratedmodelling.klab.api.lang.kim.*; +import org.integratedmodelling.klab.api.provenance.Activity; +import org.integratedmodelling.klab.api.provenance.Agent; +import org.integratedmodelling.klab.api.provenance.Provenance; import org.integratedmodelling.klab.api.services.resolver.ResolutionConstraint; import org.integratedmodelling.klab.api.services.runtime.Actuator; import org.integratedmodelling.klab.api.services.runtime.Dataflow; @@ -252,7 +255,8 @@ public static void configureObjectMapperForKlabTypes(ObjectMapper mapper) { Currency.class, Message.class, Worldview.class, Workspace.class, Concept.class, Observable.class, Resource.class, KimOntology.class, KimNamespace.class, KimObservationStrategyDocument.class, - KdlDataflow.class, KActorsBehavior.class, KimModel.class, + KActorsBehavior.class, KimModel.class, Activity.class, + Agent.class, Provenance.Node.class, Provenance.class, KimSymbolDefinition.class, Contextualizable.class, Identifier.class, KimConcept.class, KimObservable.class, Quantity.class, Model.class, ServiceCall.class, Observation.class, 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 81fe053e3..e33473862 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 @@ -11,6 +11,7 @@ import org.integratedmodelling.klab.api.data.RuntimeAsset; import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; import org.integratedmodelling.klab.api.exceptions.KlabIllegalArgumentException; +import org.integratedmodelling.klab.api.exceptions.KlabIllegalStateException; import org.integratedmodelling.klab.api.exceptions.KlabInternalErrorException; import org.integratedmodelling.klab.api.knowledge.observation.Observation; import org.integratedmodelling.klab.api.knowledge.observation.impl.ObservationImpl; @@ -271,20 +272,37 @@ public String registerContext(ContextScope contextScope) { @Override public long submit(Observation observation, ContextScope scope) { + + if (observation.isResolved()) { + // TODO there may be a context for this at some point. + throw new KlabIllegalStateException("A resolved observation cannot be submitted to the " + + "knowledge graph for now"); + } + if (scope instanceof ServiceContextScope serviceContextScope) { var digitalTwin = getDigitalTwin(scope); var parentActivity = getInstantiationActivity(observation, scope); var agent = getAgent(scope); var instantiation = digitalTwin.knowledgeGraph().operation(agent, parentActivity, Activity.Type.INSTANTIATION, observation); + try (instantiation) { var ret = instantiation.store(observation); + if (scope.getContextObservation() != null) { + instantiation.link(scope.getContextObservation(), observation, + DigitalTwin.Relationship.HAS_CHILD); + } + if (scope.getObserver() != null) { + instantiation.link(observation, scope.getObserver(), + DigitalTwin.Relationship.HAS_OBSERVER); + } instantiation.success(scope, observation); return ret; } catch (Throwable t) { instantiation.fail(scope, observation); } } + return Observation.UNASSIGNED_ID; } @@ -326,8 +344,7 @@ public Future resolve(long id, ContextScope scope) { This will commit or rollback at close() */ var resolution = digitalTwin.knowledgeGraph().operation(digitalTwin.knowledgeGraph().klab() - , parentActivity, - Activity.Type.RESOLUTION); + , parentActivity, Activity.Type.RESOLUTION); try (resolution) { result = observation; @@ -363,13 +380,12 @@ this will commit all resources at close() */ var contextualization = digitalTwin.knowledgeGraph().operation(digitalTwin.knowledgeGraph().klab(), - resolutionActivity, - Activity.Type.CONTEXTUALIZATION); + resolutionActivity, Activity.Type.CONTEXTUALIZATION); try (contextualization) { // TODO contextualization gets its own activities to use in operations // (dependent on resolution) linked to actuators by runDataflow - result = runDataflow(dataflow, scope); + result = runDataflow(dataflow, scope, contextualization); ret.complete(result); contextualization.success(scope, dataflow, result); } catch (Throwable t) { @@ -388,13 +404,12 @@ this will commit all resources at close() @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); + // TODO fill in the operation representing an external dataflow run + return runDataflow(dataflow, contextScope, null); } public Observation runDataflow(Dataflow dataflow, ContextScope contextScope, - Activity activity) { + KnowledgeGraph.Operation contextualization) { var digitalTwin = getDigitalTwin(contextScope); 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 ef90a21d3..fb7fb657c 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 @@ -46,11 +46,12 @@ public RuntimeAsset load(Long key) throws Exception { */ public class OperationImpl implements Operation { - ActivityImpl activity; - AgentImpl agent; - Transaction transaction; - Scope.Status outcome; - Throwable exception; + private ActivityImpl activity; + private AgentImpl agent; + // FIXME ACH no this must be in the Neo4j implementation + private Transaction transaction; + private Scope.Status outcome; + private Throwable exception; /** * What should be passed: an agent that will own the activity; the current context scope for graph @@ -71,7 +72,7 @@ public OperationImpl(Object... arguments) { // select arguments and put them where they belong - // validate arguments and complain loudly if anything is missing + // validate arguments and complain loudly if anything is missing. Must have agent and activity // create and commit the activity record as a node, possibly linked to a parent // activity. @@ -81,34 +82,40 @@ public OperationImpl(Object... arguments) { @Override public Agent getAgent() { - return null; + return this.agent; } @Override public Activity getActivity() { - return null; + return this.activity; } @Override public long store(RuntimeAsset asset, Object... additionalProperties) { - return 0; + // FIXME NO use transactional version! + return AbstractKnowledgeGraph.this.store(asset, scope, additionalProperties); } @Override public void link(RuntimeAsset source, RuntimeAsset destination, - DigitalTwin.Relationship relationship, Scope scope, Object... additionalProperties) { - + DigitalTwin.Relationship relationship, Object... additionalProperties) { + // FIXME NO use transactional version! + AbstractKnowledgeGraph.this.link(source, destination, relationship, scope, + additionalProperties); } @Override public Operation success(ContextScope scope, Object... assets) { - // commit - return null; + this.outcome = Scope.Status.FINISHED; + // updates as needed (activity end, observation resolved if type == resolution, context timestamp + return this; } @Override public Operation fail(ContextScope scope, Object... assets) { - // rollback + // rollback; update activity end and context timestamp only, if we have an error or throwable + // update activity + this.outcome = Scope.Status.ABORTED; return null; } @@ -116,6 +123,14 @@ public Operation fail(ContextScope scope, Object... assets) { 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 + if (outcome == null) { + // Log an internal failure (no success or failure, should not happen) + transaction.rollback(); + } else if (outcome == Scope.Status.FINISHED) { + transaction.commit(); + } else if (outcome == Scope.Status.ABORTED) { + transaction.rollback(); + } } }