diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/engine/Engine.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/engine/Engine.java index 328c4d71..85bc054f 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/engine/Engine.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/engine/Engine.java @@ -35,6 +35,31 @@ */ public interface Engine extends KlabService { + /** + * Comprehensive engine status is kept up to date by polling or listening to services. Whenever the status + * changes, either because of service lifecycle or because of the user choosing a different service as the + * current one, a message is sent (intercepted by the modeler and also sent to the UI). + */ + interface Status { + + /** + * True if every service is available and a worldview is loaded in the reasoner. + * + * @return + */ + boolean isOperational(); + + /** + * Return the current status of each specific service. If the service is not even connected, a + * non-null inactive status is returned. + * + * @param serviceType + * @return + */ + ServiceStatus getServiceStatus(KlabService.Type serviceType); + + } + /** * The engine is available to boot. * diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/KlabService.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/KlabService.java index 04b9af81..dd4df4fd 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/KlabService.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/KlabService.java @@ -153,8 +153,24 @@ interface ServiceStatus extends Serializable { String getServiceId(); + /** + * Available means that the service has been initialized and is accepting connections. It does not + * mean that the API is fully functional: it may not have gathered enough information from other + * services, or it may be configured in ways that make it inoperative. Use {@link #isOperational()} to + * check for that, and/or use the capabilities to check what functions are supported. + * + * @return + */ boolean isAvailable(); + /** + * Operational means that the entire API of the service is available to support the functions declared + * as supported in the capabilities. + * + * @return + */ + boolean isOperational(); + boolean isBusy(); /** @@ -328,7 +344,7 @@ interface ServiceCapabilities extends Serializable { /** * Register a session created by the scope manager after receiving a CREATE_SESSION request. Return a * unique session ID that may be requested with the session or generated within the service. - + * * @param sessionScope a client scope that should record the ID for future communication. If the ID is * null, the call has failed. * @return the ID of the new session created at server side, or null in case of failure. @@ -336,13 +352,13 @@ interface ServiceCapabilities extends Serializable { String registerSession(SessionScope sessionScope); /** - * Register a context scope created by the scope manager. Return a unique session ID that may be - * requested with the session or generated within the service. Context starts empty with the default - * observer for the worldview, using the services available to the user and passed as parameters. The same - * runtime that hosts the context must become the one and only runtime accessible to the resulting scope. - * If the service is not a runtime, the request must come from another service and the scope should be + * Register a context scope created by the scope manager. Return a unique session ID that may be requested + * with the session or generated within the service. Context starts empty with the default observer for + * the worldview, using the services available to the user and passed as parameters. The same runtime that + * hosts the context must become the one and only runtime accessible to the resulting scope. If the + * service is not a runtime, the request must come from another service and the scope should be * instrumented as necessary for its purposes. - + * * @param contextScope a client scope that should record the ID for future communication. If the ID is * null, the call has failed. * @return the ID of the new context scope created at server side, or null in case of failure. 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 45449c9e..73b821b9 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 @@ -986,7 +986,7 @@ interface Admin { * @return a {@link ResourceSet} including the ontologies, observation strategies, each with any * notifications created during the load process, with the lexical context set as needed */ - ResourceSet loadKnowledge(Worldview worldview, UserScope scope); + ResourceSet loadKnowledge(Worldview worldview, Scope scope); /** * When a reasoner is registered with a resources service by an external orchestrator, changes in the @@ -1014,7 +1014,7 @@ interface Admin { /** * The "port" to ingest an individual concept definition, called by - * {@link #loadKnowledge(Worldview, UserScope)} (Worldview)}. Provided separately to make it possible + * {@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. @@ -1023,7 +1023,7 @@ interface Admin { * @param scope admin user scope to report and validate * @return */ - Concept defineConcept(KimConceptStatement statement, UserScope scope); + Concept defineConcept(KimConceptStatement statement, Scope scope); /** * Export a namespace as an OWL ontology with all dependencies. diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/ResourcesService.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/ResourcesService.java index a72d9b09..d004280a 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/ResourcesService.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/ResourcesService.java @@ -82,6 +82,14 @@ interface Capabilities extends ServiceCapabilities { boolean isWorldviewProvider(); + /** + * If true, the service is connected to an operational reasoner and can support semantically aware + * calls such as {@link #queryModels(Observable, ContextScope)}. + * + * @return + */ + boolean isSemanticSearchCapable(); + String getAdoptedWorldview(); /** diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/impl/ServiceStatusImpl.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/impl/ServiceStatusImpl.java index 612d5077..714ec9e9 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/impl/ServiceStatusImpl.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/impl/ServiceStatusImpl.java @@ -23,6 +23,7 @@ public class ServiceStatusImpl implements KlabService.ServiceStatus { private boolean available = false; private boolean busy = false; private boolean consistent = true; + private boolean operational = false; private ServiceScope.Locality locality = ServiceScope.Locality.EMBEDDED; private KlabService.Type serviceType; private String serviceId; @@ -167,6 +168,15 @@ public boolean isConsistent() { return consistent; } + @Override + public boolean isOperational() { + return operational; + } + + public void setOperational(boolean operational) { + this.operational = operational; + } + public void setConsistent(boolean consistent) { this.consistent = consistent; } diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/runtime/Message.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/runtime/Message.java index b4a096fe..4b6df159 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/runtime/Message.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/runtime/Message.java @@ -1,6 +1,7 @@ package org.integratedmodelling.klab.api.services.runtime; import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; +import org.integratedmodelling.klab.api.engine.Engine; import org.integratedmodelling.klab.api.engine.distribution.Distribution; import org.integratedmodelling.klab.api.identities.UserIdentity; import org.integratedmodelling.klab.api.lang.kactors.beans.ActionStatistics; @@ -184,7 +185,7 @@ enum MessageType { * Service messages, coming with service capabilities */ ServiceInitializing(Queue.Events, KlabService.ServiceCapabilities.class), - ReasoningAvailable(Queue.Events, Reasoner.Capabilities.class), +// ReasoningAvailable(Queue.Events, Reasoner.Capabilities.class), ServiceAvailable(Queue.Events, KlabService.ServiceCapabilities.class), ServiceUnavailable(Queue.Events, KlabService.ServiceCapabilities.class), ServiceStatus(Queue.Events, KlabService.ServiceStatus.class), @@ -220,10 +221,10 @@ enum MessageType { Warning(Queue.Warnings, Notification.class), Error(Queue.Errors, Notification.class), - /* - * --- reasoning-related messages - */ - LogicalValidation(Queue.Events,ResourceSet.class), +// /* +// * --- reasoning-related messages +// */ +// LogicalValidation(Queue.Events,ResourceSet.class), /** * Runtime event messages @@ -245,6 +246,11 @@ enum MessageType { ResolutionAborted(Queue.Events, Long.class), + /** + * Engine status has changed + */ + EngineStatusChanged(Queue.Events, Engine.Status.class), + /* * --- View actor messages */ diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/view/UIReactor.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/view/UIReactor.java index 8e276aa9..bd23abf0 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/view/UIReactor.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/view/UIReactor.java @@ -1,6 +1,7 @@ package org.integratedmodelling.klab.api.view; import org.integratedmodelling.klab.api.data.mediation.classification.Classification; +import org.integratedmodelling.klab.api.engine.Engine; import org.integratedmodelling.klab.api.engine.distribution.Distribution; import org.integratedmodelling.klab.api.identities.UserIdentity; import org.integratedmodelling.klab.api.knowledge.Expression; @@ -160,27 +161,28 @@ enum UIAction { */ enum UIEvent { - EngineStarting(EventDirection.EngineToView, UserIdentity.class), - /** - * From this point on, the UI should be synced to the services in the scope and made its functions - * available based on the current service capabilities. - */ - EngineAvailable(EventDirection.EngineToView, UserScope.class), - EngineUnavailable(EventDirection.EngineToView, UserIdentity.class), - /** - * Sent only for the DEFAULT services in case they are local to the modeler's instance. - */ - ServiceStarting(EventDirection.EngineToView, KlabService.ServiceCapabilities.class), - /** - * Sent only for the DEFAULT services in case they are local to the modeler's instance or for other - * services in case they come online after going offline. Besides a default/local service, the - * services listed in the UI should be those listed in the user scope sent with EngineAvailable. - */ - ServiceAvailable(EventDirection.EngineToView, KlabService.ServiceCapabilities.class), - /** - * Sent for any of the services in the user scope when they go offline. - */ - ServiceUnavailable(EventDirection.EngineToView, KlabService.ServiceCapabilities.class), +// EngineStarting(EventDirection.EngineToView, UserIdentity.class), +// /** +// * From this point on, the UI should be synced to the services in the scope and made its functions +// * available based on the current service capabilities. +// */ +// EngineAvailable(EventDirection.EngineToView, UserScope.class), +// EngineUnavailable(EventDirection.EngineToView, UserIdentity.class), + EngineStatusChanged(EventDirection.EngineToView, Engine.Status.class), +// /** +// * Sent only for the DEFAULT services in case they are local to the modeler's instance. +// */ +// ServiceStarting(EventDirection.EngineToView, KlabService.ServiceCapabilities.class), +// /** +// * Sent only for the DEFAULT services in case they are local to the modeler's instance or for other +// * services in case they come online after going offline. Besides a default/local service, the +// * services listed in the UI should be those listed in the user scope sent with EngineAvailable. +// */ +// ServiceAvailable(EventDirection.EngineToView, KlabService.ServiceCapabilities.class), +// /** +// * Sent for any of the services in the user scope when they go offline. +// */ +// ServiceUnavailable(EventDirection.EngineToView, KlabService.ServiceCapabilities.class), /** * Sent from the UI to the engine to define which service to use as the default for the class, and to @@ -234,7 +236,7 @@ enum UIEvent { ImportProjectRequest(EventDirection.ViewToEngine, String.class), Notification(EventDirection.Bidirectional, Notification.class), DistributionSelected(EventDirection.ViewToView, Distribution.class), - ReasoningAvailable(EventDirection.EngineToView, Reasoner.Capabilities.class), +// ReasoningAvailable(EventDirection.EngineToView, Reasoner.Capabilities.class), UserAuthenticated(EventDirection.Bidirectional, UserIdentity.class), /** diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/view/modeler/views/controllers/ServicesViewController.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/view/modeler/views/controllers/ServicesViewController.java index 85bd0663..23de6530 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/view/modeler/views/controllers/ServicesViewController.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/view/modeler/views/controllers/ServicesViewController.java @@ -25,27 +25,31 @@ public interface ServicesViewController extends ViewController { * * @param service */ - @UIEventHandler(UIEvent.ServiceAvailable) - void serviceAvailable(KlabService.ServiceCapabilities service); + // @UIEventHandler(UIEvent.ServiceAvailable) + // void serviceAvailable(KlabService.ServiceCapabilities service); + // + // @UIEventHandler(UIEvent.ServiceUnavailable) + // void serviceUnavailable(KlabService.ServiceCapabilities service); + // + // @UIEventHandler(UIEvent.ServiceStarting) + // void serviceStarting(KlabService.ServiceCapabilities service); + // + // /** + // * Will be called at least once to report on service status, and possibly at regular intervals + // depending + // * on the engine implementation. This may make the service available, unavailable, busy, and + // report on + // * current load factor and state. + // * + // * @param status + // */ + // @UIEventHandler(UIEvent.ServiceStatus) + // void serviceStatus(KlabService.ServiceStatus status); - @UIEventHandler(UIEvent.ServiceUnavailable) - void serviceUnavailable(KlabService.ServiceCapabilities service); - - @UIEventHandler(UIEvent.ServiceStarting) - void serviceStarting(KlabService.ServiceCapabilities service); - - /** - * Will be called at least once to report on service status, and possibly at regular intervals depending - * on the engine implementation. This may make the service available, unavailable, busy, and report on - * current load factor and state. - * - * @param status - */ - @UIEventHandler(UIEvent.ServiceStatus) - void serviceStatus(KlabService.ServiceStatus status); - - @UIEventHandler(UIEvent.ReasoningAvailable) - void reasoningAvailable(Reasoner.Capabilities capabilities); + // @UIEventHandler(UIEvent.ReasoningAvailable) + // void reasoningAvailable(Reasoner.Capabilities capabilities); + @UIEventHandler(UIEvent.EngineStatusChanged) + void engineStatusChanged(Engine.Status status); /** * User action choosing a service to focus on. The view handles all the UI implications and then calls diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/services/ResourcesCapabilitiesImpl.java b/klab.core.common/src/main/java/org/integratedmodelling/common/services/ResourcesCapabilitiesImpl.java index d5c54012..cf3fe189 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/services/ResourcesCapabilitiesImpl.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/services/ResourcesCapabilitiesImpl.java @@ -17,6 +17,7 @@ public class ResourcesCapabilitiesImpl extends AbstractServiceCapabilities imple private List workspaceNames = new ArrayList<>(); private Set permissions = EnumSet.of(CRUDOperation.READ); private List serviceNotifications = new ArrayList<>(); + private boolean semanticSearchCapable; @Override public KlabService.Type getType() { @@ -71,4 +72,12 @@ public void setPermissions(Set permissions) { this.permissions = permissions; } + @Override + public boolean isSemanticSearchCapable() { + return semanticSearchCapable; + } + + public void setSemanticSearchCapable(boolean semanticSearchCapable) { + this.semanticSearchCapable = semanticSearchCapable; + } } diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/ServiceClient.java b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/ServiceClient.java index 867568c3..e86421ca 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/ServiceClient.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/ServiceClient.java @@ -57,7 +57,7 @@ public abstract class ServiceClient implements KlabService { private AbstractServiceDelegatingScope scope; private URL url; private String token; - private long pollCycleSeconds = 2; + private long pollCycleSeconds = 5; protected Utils.Http.Client client; private ServiceCapabilities capabilities; // this is not null only if the client is used by another service. In that case, the service should be diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/engine/EngineClient.java b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/engine/EngineClient.java index 88a8c075..bf50aa5b 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/engine/EngineClient.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/engine/EngineClient.java @@ -29,6 +29,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; /** @@ -58,9 +59,10 @@ public class EngineClient implements Engine, PropertyHolder { ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private boolean firstCall = true; String serviceId = Utils.Names.shortUUID(); - private boolean reasoningAvailable; - private boolean reasonerDisabled; +// private boolean reasoningAvailable; +// private boolean reasonerDisabled; private Worldview worldview; + private AtomicReference status = new AtomicReference<>(EngineStatusImpl.inop()); public UserScope getUser() { return !this.users.isEmpty() ? users.getFirst() : null; @@ -244,14 +246,13 @@ private void timedTasks() { firstCall = false; } + recomputeEngineStatus(); + /** * Check if we have reasoning until we do */ - if (!reasoningAvailable && !reasonerDisabled) { - /** - * If we have a worldview from the resources service and the reasoner is exclusive and - * doesn't have a worldview, load the worldview in the reasoner. - */ + /* if (!reasoningAvailable && !reasonerDisabled) { + var reasoner = serviceScope().getService(Reasoner.class); if (reasoner != null && reasoner.status().isAvailable() && reasoner.capabilities(serviceScope()).getWorldviewId() != null) { @@ -281,9 +282,8 @@ private void timedTasks() { serviceScope().info("Worldview loaded into local reasoner"); } } - } - } - + }*/ +// } // inform listeners if (wasAvailable != ok) { @@ -299,6 +299,21 @@ private void timedTasks() { available.set(ok); } + private synchronized void recomputeEngineStatus() { + + // explore state of all services, determine what we + EngineStatusImpl engineStatus = new EngineStatusImpl(); + + // TODO fill this in, assess operational status w.r.t. current services etc. + + // if state has changed, swap and send message + if (this.status.get() == null || !EngineStatusImpl.equals(this.status.get(), engineStatus)) { + this.status.set(engineStatus); + serviceScope().send(Message.MessageClass.EngineLifecycle, + Message.MessageType.EngineStatusChanged, engineStatus); + } + } + private void registerService(KlabService.Type serviceType, KlabService service) { if (!currentServices.containsKey(serviceType)) { currentServices.put(serviceType, service); diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/engine/EngineStatusImpl.java b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/engine/EngineStatusImpl.java new file mode 100644 index 00000000..d26b2164 --- /dev/null +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/engine/EngineStatusImpl.java @@ -0,0 +1,46 @@ +package org.integratedmodelling.common.services.client.engine; + +import org.integratedmodelling.klab.api.engine.Engine; +import org.integratedmodelling.klab.api.services.KlabService; + +import java.util.HashMap; +import java.util.Map; + +public class EngineStatusImpl implements Engine.Status { + + private Map serviceStatusMap = new HashMap<>(); + private boolean operational; + + @Override + public boolean isOperational() { + return operational; + } + + @Override + public KlabService.ServiceStatus getServiceStatus(KlabService.Type serviceType) { + // TODO return inop service status if null + return serviceStatusMap.get(serviceType); + } + + public static Engine.Status inop() { + return new EngineStatusImpl(); + } + + public void setOperational(boolean operational) { + this.operational = operational; + } + + public Map getServiceStatusMap() { + return serviceStatusMap; + } + + public void setServiceStatusMap(Map serviceStatusMap) { + this.serviceStatusMap = serviceStatusMap; + } + + public static boolean equals(Engine.Status s1, Engine.Status s2) { + // TODO + return false; + } + +} diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/reasoner/ReasonerClient.java b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/reasoner/ReasonerClient.java index 7ae6bfb4..c627c4b7 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/reasoner/ReasonerClient.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/reasoner/ReasonerClient.java @@ -593,7 +593,7 @@ public boolean resolves(Semantics toResolve, Semantics candidate, Semantics cont @Override - public ResourceSet loadKnowledge(Worldview worldview, UserScope scope) { + public ResourceSet loadKnowledge(Worldview worldview, Scope scope) { return client.post(ServicesAPI.REASONER.ADMIN.LOAD_KNOWLEDGE, worldview, ResourceSet.class); } @@ -603,7 +603,7 @@ public ResourceSet updateKnowledge(ResourceSet changes, UserScope scope) { } @Override - public Concept defineConcept(KimConceptStatement statement, UserScope scope) { + public Concept defineConcept(KimConceptStatement statement, Scope scope) { return client.post(ServicesAPI.REASONER.ADMIN.DEFINE_CONCEPT, statement, Concept.class); } diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/view/AbstractUIController.java b/klab.core.common/src/main/java/org/integratedmodelling/common/view/AbstractUIController.java index 9b24a68c..41b17ddd 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/view/AbstractUIController.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/view/AbstractUIController.java @@ -202,14 +202,12 @@ protected void processMessage(Channel scope, Message message) { } case ServiceLifecycle -> { switch (message.getMessageType()) { - case ServiceUnavailable -> dispatch(this, UIReactor.UIEvent.ServiceUnavailable, - message.getPayload(Object.class)); - case ServiceAvailable -> dispatch(this, UIReactor.UIEvent.ServiceAvailable, - message.getPayload(Object.class)); - case ServiceInitializing -> dispatch(this, UIReactor.UIEvent.ServiceStarting, - message.getPayload(Object.class)); - case ServiceStatus -> dispatch(this, UIReactor.UIEvent.ServiceStatus, - message.getPayload(KlabService.ServiceStatus.class)); + case ServiceUnavailable, ServiceAvailable, ServiceInitializing -> recomputeStatus(message); + case ServiceStatus -> { + recomputeStatus(message); + dispatch(this, UIReactor.UIEvent.ServiceStatus, + message.getPayload(KlabService.ServiceStatus.class)); + } default -> { } } @@ -217,16 +215,11 @@ protected void processMessage(Channel scope, Message message) { case EngineLifecycle -> { // TODO engine ready event and status switch (message.getMessageType()) { - case ServiceUnavailable -> { - dispatch(this, UIReactor.UIEvent.EngineUnavailable, message.getPayload(Object.class)); - } - case ServiceAvailable -> { - dispatch(this, UIReactor.UIEvent.EngineAvailable, message.getPayload(Object.class)); - } - case ServiceInitializing -> { - dispatch(this, UIReactor.UIEvent.EngineStarting, message.getPayload(Object.class)); + case ServiceUnavailable, ServiceAvailable, ServiceInitializing -> { + recomputeStatus(message); } case ServiceStatus -> { + recomputeStatus(message); dispatch(this, UIReactor.UIEvent.ServiceStatus, message.getPayload(KlabService.ServiceStatus.class)); } @@ -234,10 +227,10 @@ protected void processMessage(Channel scope, Message message) { dispatch(this, UIReactor.UIEvent.DistributionSelected, message.getPayload(Distribution.class)); } - case ReasoningAvailable -> { - dispatch(this, UIReactor.UIEvent.ReasoningAvailable, - message.getPayload(Reasoner.Capabilities.class)); - } +// case ReasoningAvailable -> { +// dispatch(this, UIReactor.UIEvent.ReasoningAvailable, +// message.getPayload(Reasoner.Capabilities.class)); +// } default -> { } @@ -280,13 +273,19 @@ protected void processMessage(Channel scope, Message message) { case ActorCommunication -> { } case KnowledgeLifecycle -> { - if (message.is(Message.MessageType.LogicalValidation)) { - dispatch(this, UIEvent.LogicalValidation, message.getPayload(ResourceSet.class)); - } +// if (message.is(Message.MessageType.LogicalValidation)) { +// dispatch(this, UIEvent.LogicalValidation, message.getPayload(ResourceSet.class)); +// } } } } + private void recomputeStatus(Message message) { + + System.out.println(" HOSTIA RECOMPUTE STATUS DIOCA' " + message); + + } + @Override public void dispatch(UIReactor sender, UIReactor.UIEvent event, Object... payload) { // TODO handle reactors to UiEvent.Any diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/ServiceInstance.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/ServiceInstance.java index 60f797f2..f27d504b 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/ServiceInstance.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/ServiceInstance.java @@ -380,8 +380,7 @@ private void timedTasks() { if (okEssentials && okOperationals && !operationalized.get()) { setBusy(true); operationalized.set(true); - klabService().operationalizeService(); - klabService().setOperational(true); + klabService().setOperational(klabService().operationalizeService()); setBusy(false); } } catch (Throwable t) { diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/base/BaseService.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/base/BaseService.java index bfa3c481..b2e44afb 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/base/BaseService.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/base/BaseService.java @@ -182,6 +182,7 @@ public ServiceStatus status() { ret.setAvailable(initialized && serviceScope().isAvailable()); ret.setBusy(serviceScope().isBusy()); ret.setLocality(serviceScope().getLocality()); + ret.setOperational(operational); return ret; } @@ -244,9 +245,11 @@ public AbstractServiceDelegatingScope serviceScope() { public abstract void initializeService(); /** - * Called when all non-essential operational services become available. + * Called when all non-essential operational services become available. The return value will be the + * operational status returned in {@link #status()}. Operational means that the API is usable as + * advertised in {@link #capabilities(Scope)}. */ - public abstract void operationalizeService(); + public abstract boolean operationalizeService(); @Override @@ -308,6 +311,9 @@ public boolean isExclusive() { return false; } + protected boolean isOperational() { + return operational; + } @Override public ResourcePrivileges getRights(String resourceUrn, Scope scope) { @@ -368,6 +374,7 @@ public String registerContext(ContextScope contextScope) { public void setOperational(boolean b) { this.operational = true; } + /** * Called by ServiceInstance after initializeService was successful * diff --git a/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/ModelerImpl.java b/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/ModelerImpl.java index b0c68933..3793c1ed 100644 --- a/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/ModelerImpl.java +++ b/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/ModelerImpl.java @@ -84,26 +84,31 @@ public ModelerImpl(UI ui) { public void dispatch(UIReactor sender, UIEvent event, Object... payload) { // intercept some messages for bookkeeping - if (event == UIEvent.ServiceAvailable && payload.length > 0 && payload[0] instanceof KlabService.ServiceCapabilities capabilities) { - if (capabilities.getUrl() != null) { - serviceUrls.put(capabilities.getServiceId(), capabilities.getUrl()); - } - if (capabilities.getBrokerURI() != null && scope() instanceof AbstractReactiveScopeImpl serviceClient) { - /* - * Instrument the service client for messaging. This is pretty involved alas, but the whole - * matter isn't exactly trivial. - */ - var client = serviceClient.getService(capabilities.getServiceId()); - if (client != null && client.serviceScope() instanceof AbstractServiceDelegatingScope delegatingScope - && delegatingScope.getDelegateChannel() instanceof MessagingChannel messagingChannel) { - /* - * If the scope delegates to a messaging channel, set up messaging and link the - * available service queues to service message dispatchers. - */ - messagingChannel.connectToService(capabilities, (UserIdentity) user().getIdentity(), - (message) -> dispatchServerMessage(capabilities, message)); - } - } + if (event == UIEvent.EngineStatusChanged) { + + System.out.println("CAMBIATO STATO DIOCAN " + payload[0]); + +// for (var service : ) +// +// if (capabilities.getUrl() != null) { +// serviceUrls.put(capabilities.getServiceId(), capabilities.getUrl()); +// } +// if (capabilities.getBrokerURI() != null && scope() instanceof AbstractReactiveScopeImpl serviceClient) { +// /* +// * Instrument the service client for messaging. This is pretty involved alas, but the whole +// * matter isn't exactly trivial. +// */ +// var client = serviceClient.getService(capabilities.getServiceId()); +// if (client != null && client.serviceScope() instanceof AbstractServiceDelegatingScope delegatingScope +// && delegatingScope.getDelegateChannel() instanceof MessagingChannel messagingChannel) { +// /* +// * If the scope delegates to a messaging channel, set up messaging and link the +// * available service queues to service message dispatchers. +// */ +// messagingChannel.connectToService(capabilities, (UserIdentity) user().getIdentity(), +// (message) -> dispatchServerMessage(capabilities, message)); +// } +// } } super.dispatch(sender, event, payload); diff --git a/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/views/controllers/ResourcesNavigatorControllerImpl.java b/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/views/controllers/ResourcesNavigatorControllerImpl.java index 36f47302..0dd748aa 100644 --- a/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/views/controllers/ResourcesNavigatorControllerImpl.java +++ b/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/views/controllers/ResourcesNavigatorControllerImpl.java @@ -65,7 +65,7 @@ public void workspaceModified(ResourceSet changes) { if (reasoner.isExclusive() && reasoner instanceof Reasoner.Admin adminReasoner && !Worldview.WORLDVIEW_WORKSPACE_IDENTIFIER.equals(changes.getWorkspace())) { var logicalChanges = adminReasoner.updateKnowledge(changes,getController().user()); if (!logicalChanges.isEmpty()) { - getController().engine().serviceScope().send(Message.MessageClass.KnowledgeLifecycle, Message.MessageType.LogicalValidation, logicalChanges); + getController().dispatch(this, UIEvent.LogicalValidation, logicalChanges); } } } diff --git a/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/views/controllers/ServicesViewControllerImpl.java b/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/views/controllers/ServicesViewControllerImpl.java index 5c2967b0..1da4b10d 100644 --- a/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/views/controllers/ServicesViewControllerImpl.java +++ b/klab.modeler/src/main/java/org/integratedmodelling/klab/modeler/views/controllers/ServicesViewControllerImpl.java @@ -1,6 +1,7 @@ package org.integratedmodelling.klab.modeler.views.controllers; import org.integratedmodelling.common.view.AbstractUIViewController; +import org.integratedmodelling.klab.api.engine.Engine; import org.integratedmodelling.klab.api.engine.distribution.RunningInstance; import org.integratedmodelling.klab.api.services.KlabService; import org.integratedmodelling.klab.api.services.Reasoner; @@ -17,33 +18,38 @@ public ServicesViewControllerImpl(UIController controller) { super(controller); } - @Override - public void serviceAvailable(KlabService.ServiceCapabilities service) { - // TODO add to engine, select if first or configured default - view().servicesConfigurationChanged(service, RunningInstance.Status.RUNNING); - } - - @Override - public void serviceUnavailable(KlabService.ServiceCapabilities service) { - // TODO add to engine, select if first or configured default - view().servicesConfigurationChanged(service, RunningInstance.Status.STOPPED); - } - - @Override - public void serviceStarting(KlabService.ServiceCapabilities service) { - // TODO add to engine, select if first or configured default - view().servicesConfigurationChanged(service, RunningInstance.Status.WAITING); - } - - @Override - public void serviceStatus(KlabService.ServiceStatus status) { - // TODO add to engine, select if first or configured default - view().notifyServiceStatus(status); - } +// @Override +// public void serviceAvailable(KlabService.ServiceCapabilities service) { +// // TODO add to engine, select if first or configured default +// view().servicesConfigurationChanged(service, RunningInstance.Status.RUNNING); +// } +// +// @Override +// public void serviceUnavailable(KlabService.ServiceCapabilities service) { +// // TODO add to engine, select if first or configured default +// view().servicesConfigurationChanged(service, RunningInstance.Status.STOPPED); +// } +// +// @Override +// public void serviceStarting(KlabService.ServiceCapabilities service) { +// // TODO add to engine, select if first or configured default +// view().servicesConfigurationChanged(service, RunningInstance.Status.WAITING); +// } +// +// @Override +// public void serviceStatus(KlabService.ServiceStatus status) { +// // TODO add to engine, select if first or configured default +// view().notifyServiceStatus(status); +// } +// +// @Override +// public void reasoningAvailable(Reasoner.Capabilities capabilities) { +// view().reasoningAvailable(capabilities); +// } @Override - public void reasoningAvailable(Reasoner.Capabilities capabilities) { - view().reasoningAvailable(capabilities); + public void engineStatusChanged(Engine.Status status) { + // TODO all the above as needed } @Override diff --git a/klab.services.community/src/main/java/org/integratedmodelling/klab/services/community/CommunityService.java b/klab.services.community/src/main/java/org/integratedmodelling/klab/services/community/CommunityService.java index 9b633006..8dc75497 100644 --- a/klab.services.community/src/main/java/org/integratedmodelling/klab/services/community/CommunityService.java +++ b/klab.services.community/src/main/java/org/integratedmodelling/klab/services/community/CommunityService.java @@ -23,8 +23,8 @@ public void initializeService() { } @Override - public void operationalizeService() { - + public boolean operationalizeService() { + return true; } @Override 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 01bcc569..3d165aa9 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 @@ -305,14 +305,54 @@ public void initializeService() { "offline")); } + /* + This is called when resources are available, so this is the time to load the worldview. + */ + for (var resources : serviceScope().getServices(ResourcesService.class)) { + if (resources.status().isAvailable() && resources.capabilities(serviceScope()).isWorldviewProvider()) { + + var notifications = loadKnowledge(resources.getWorldview(), serviceScope()); + + if (!org.integratedmodelling.klab.api.utils.Utils.Resources.hasErrors(notifications)) { + // setOperational(false); + // serviceScope().warn("Worldview loading failed: reasoner is + // disabled"); + // } else { + setOperational(true); +// // FIXME not sure this is useful anymore +// serviceScope().send(Message.MessageClass.EngineLifecycle, +// Message.MessageType.ReasoningAvailable, +// capabilities(serviceScope())); + serviceScope().info("Worldview loaded into local reasoner"); + + // TODO if there were previous logical notifications they should be deleted now + + // worldview is synchronized, so the first one that works is the one + break; + + }/* else { + // FIXME this isn't reaching the clients as it should in a modeler config. Should just + // let the client ask for notifications (at that point they can be in capabilities) if + // status is not operational. +// serviceScope().send(Message.MessageClass.KnowledgeLifecycle, +// Message.MessageType.LogicalValidation, notifications); + + }*/ + } + } + + // TODO keep logical notifications around for the capabilities, or have a separate status call for + // notifications. serviceScope().send(Message.MessageClass.ServiceLifecycle, Message.MessageType.ServiceAvailable, capabilities(serviceScope())); } @Override - public void operationalizeService() { - + public boolean operationalizeService() { + // we have done what we needed, just return the outcome. Basically we're not operational unless we + // have a valid worldview. + return isOperational(); } @SuppressWarnings("unchecked") @@ -340,7 +380,7 @@ private void saveConfiguration() { } @Override - public Concept defineConcept(KimConceptStatement statement, UserScope scope) { + public Concept defineConcept(KimConceptStatement statement, Scope scope) { return build(statement, this.owl.requireOntology(statement.getNamespace(), OWL.DEFAULT_ONTOLOGY_PREFIX), null, scope); } @@ -1304,7 +1344,7 @@ public Collection directRoles(Semantics concept) { } @Override - public ResourceSet loadKnowledge(Worldview worldview, UserScope scope) { + public ResourceSet loadKnowledge(Worldview worldview, Scope scope) { List ret = new ArrayList<>(); @@ -1531,7 +1571,7 @@ public Observable declareObservable(KimObservable observableDeclaration, @Override public Concept declareConcept(KimConcept observableDeclaration, - Map patternVariables) { + Map patternVariables) { if (!observableDeclaration.isPattern()) { return declareConcept(observableDeclaration); @@ -2616,7 +2656,8 @@ public boolean shutdown() { } @Override - public List computeObservationStrategies(Observation observation, ContextScope scope) { + public List computeObservationStrategies(Observation observation, + ContextScope scope) { return observationReasoner.computeMatchingStrategies(observation, scope); } diff --git a/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java b/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java index 4cefb306..54a5794c 100644 --- a/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java +++ b/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java @@ -880,8 +880,8 @@ public void initializeService() { } @Override - public void operationalizeService() { - + public boolean operationalizeService() { + return true; } @Override diff --git a/klab.services.resources/src/main/java/org/integratedmodelling/klab/services/resources/ResourcesProvider.java b/klab.services.resources/src/main/java/org/integratedmodelling/klab/services/resources/ResourcesProvider.java index c0dc7fe2..3cffe23b 100644 --- a/klab.services.resources/src/main/java/org/integratedmodelling/klab/services/resources/ResourcesProvider.java +++ b/klab.services.resources/src/main/java/org/integratedmodelling/klab/services/resources/ResourcesProvider.java @@ -25,6 +25,7 @@ import org.integratedmodelling.klab.api.lang.kdl.KdlDataflow; import org.integratedmodelling.klab.api.lang.kim.*; import org.integratedmodelling.klab.api.scope.*; +import org.integratedmodelling.klab.api.services.Reasoner; import org.integratedmodelling.klab.api.services.ResourcesService; import org.integratedmodelling.klab.api.services.resolver.Coverage; import org.integratedmodelling.klab.api.services.resources.ResourceSet; @@ -92,6 +93,8 @@ public class ResourcesProvider extends BaseService implements ResourcesService, private DB db = null; private ConcurrentNavigableMap catalog = null; private ModelKbox kbox; + // set to true when the connected reasoner becomes operational + private boolean semanticSearchAvailable = false; /* * "fair" read/write lock to ensure no reading during updates @@ -161,10 +164,17 @@ public void initializeService() { } @Override - public void operationalizeService() { - // reasoner is available, index the kbox - Logging.INSTANCE.info("Reasoner is available: indexing semantic assets"); - indexKnowledge(); + public boolean operationalizeService() { + var reasoner = serviceScope().getService(Reasoner.class); + if (reasoner.status().isOperational()) { + Logging.INSTANCE.info("Reasoner is available: indexing semantic assets"); + indexKnowledge(); + this.semanticSearchAvailable = true; + } else { + Logging.INSTANCE.warn("reasoner is inoperative: cannot index semantic content"); + this.semanticSearchAvailable = false; + } + return true; } /** @@ -721,6 +731,11 @@ public void setLocalName(String localName) { @Override public ResourceSet queryModels(Observable observable, ContextScope scope) { + if (!semanticSearchAvailable) { + Logging.INSTANCE.warn("Semantic search is not available: client should not make this request"); + return ResourceSet.empty(); + } + ResourceSet results = new ResourceSet(); // FIXME use the observation's scale (pass the observation) for (ModelReference model : this.kbox.query(observable, scope)) { 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 68c7f9a3..e79ad8ad 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 @@ -136,8 +136,9 @@ public void initializeService() { } @Override - public void operationalizeService() { + public boolean operationalizeService() { // nothing to do here + return true; } @Override