diff options
15 files changed, 379 insertions, 232 deletions
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java index 2495430e..39191162 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java | |||
@@ -5,8 +5,6 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.language.web.semantics; | 6 | package tools.refinery.language.web.semantics; |
7 | 7 | ||
8 | import com.google.gson.JsonArray; | ||
9 | import com.google.gson.JsonObject; | ||
10 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
11 | import com.google.inject.Provider; | 9 | import com.google.inject.Provider; |
12 | import com.google.inject.Singleton; | 10 | import com.google.inject.Singleton; |
@@ -14,43 +12,29 @@ import org.eclipse.xtext.service.OperationCanceledManager; | |||
14 | import org.eclipse.xtext.util.CancelIndicator; | 12 | import org.eclipse.xtext.util.CancelIndicator; |
15 | import org.eclipse.xtext.web.server.model.AbstractCachedService; | 13 | import org.eclipse.xtext.web.server.model.AbstractCachedService; |
16 | import org.eclipse.xtext.web.server.model.IXtextWebDocument; | 14 | import org.eclipse.xtext.web.server.model.IXtextWebDocument; |
17 | import org.eclipse.xtext.web.server.model.XtextWebDocument; | ||
18 | import org.eclipse.xtext.web.server.validation.ValidationService; | 15 | import org.eclipse.xtext.web.server.validation.ValidationService; |
19 | import org.jetbrains.annotations.Nullable; | 16 | import org.jetbrains.annotations.Nullable; |
20 | import org.slf4j.Logger; | 17 | import org.slf4j.Logger; |
21 | import org.slf4j.LoggerFactory; | 18 | import org.slf4j.LoggerFactory; |
22 | import tools.refinery.language.model.problem.Problem; | 19 | import tools.refinery.language.model.problem.Problem; |
23 | import tools.refinery.language.semantics.model.ModelInitializer; | 20 | import tools.refinery.language.web.xtext.server.push.PushWebDocument; |
24 | import tools.refinery.language.semantics.model.SemanticsUtils; | ||
25 | import tools.refinery.store.model.Model; | ||
26 | import tools.refinery.store.model.ModelStore; | ||
27 | import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; | ||
28 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
29 | import tools.refinery.store.reasoning.ReasoningStoreAdapter; | ||
30 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
31 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
32 | import tools.refinery.store.representation.TruthValue; | ||
33 | import tools.refinery.store.tuple.Tuple; | ||
34 | 21 | ||
35 | import java.util.Arrays; | 22 | import java.util.concurrent.*; |
36 | import java.util.List; | ||
37 | import java.util.TreeMap; | ||
38 | 23 | ||
39 | @Singleton | 24 | @Singleton |
40 | public class SemanticsService extends AbstractCachedService<SemanticsResult> { | 25 | public class SemanticsService extends AbstractCachedService<SemanticsResult> { |
41 | private static final Logger LOG = LoggerFactory.getLogger(SemanticsService.class); | 26 | private static final Logger LOG = LoggerFactory.getLogger(SemanticsService.class); |
42 | 27 | ||
43 | @Inject | 28 | @Inject |
44 | private SemanticsUtils semanticsUtils; | 29 | private Provider<SemanticsWorker> workerProvider; |
45 | 30 | ||
46 | @Inject | 31 | @Inject |
47 | private ValidationService validationService; | 32 | private OperationCanceledManager operationCanceledManager; |
48 | 33 | ||
49 | @Inject | 34 | @Inject |
50 | private Provider<ModelInitializer> initializerProvider; | 35 | private ValidationService validationService; |
51 | 36 | ||
52 | @Inject | 37 | private final ExecutorService executorService = Executors.newCachedThreadPool(); |
53 | private OperationCanceledManager operationCanceledManager; | ||
54 | 38 | ||
55 | @Override | 39 | @Override |
56 | public SemanticsResult compute(IXtextWebDocument doc, CancelIndicator cancelIndicator) { | 40 | public SemanticsResult compute(IXtextWebDocument doc, CancelIndicator cancelIndicator) { |
@@ -58,44 +42,42 @@ public class SemanticsService extends AbstractCachedService<SemanticsResult> { | |||
58 | if (LOG.isTraceEnabled()) { | 42 | if (LOG.isTraceEnabled()) { |
59 | start = System.currentTimeMillis(); | 43 | start = System.currentTimeMillis(); |
60 | } | 44 | } |
61 | Problem problem = getProblem(doc, cancelIndicator); | 45 | var problem = getProblem(doc, cancelIndicator); |
62 | if (problem == null) { | 46 | if (problem == null) { |
63 | return null; | 47 | return null; |
64 | } | 48 | } |
65 | var initializer = initializerProvider.get(); | 49 | var worker = workerProvider.get(); |
66 | var builder = ModelStore.builder() | 50 | worker.setProblem(problem,cancelIndicator); |
67 | .with(ViatraModelQueryAdapter.builder()) | 51 | var future = executorService.submit(worker); |
68 | .with(ReasoningAdapter.builder() | 52 | SemanticsResult result = null; |
69 | .requiredInterpretations(Concreteness.PARTIAL)); | ||
70 | operationCanceledManager.checkCanceled(cancelIndicator); | ||
71 | try { | 53 | try { |
72 | var modelSeed = initializer.createModel(problem, builder); | 54 | result = future.get(2, TimeUnit.SECONDS); |
73 | operationCanceledManager.checkCanceled(cancelIndicator); | 55 | } catch (InterruptedException e) { |
74 | var nodeTrace = getNodeTrace(initializer); | 56 | future.cancel(true); |
75 | operationCanceledManager.checkCanceled(cancelIndicator); | 57 | LOG.error("Semantics service interrupted", e); |
76 | var store = builder.build(); | 58 | Thread.currentThread().interrupt(); |
77 | operationCanceledManager.checkCanceled(cancelIndicator); | 59 | } catch (ExecutionException e) { |
78 | var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); | 60 | operationCanceledManager.propagateAsErrorIfCancelException(e.getCause()); |
79 | operationCanceledManager.checkCanceled(cancelIndicator); | 61 | throw new IllegalStateException(e); |
80 | var partialInterpretation = getPartialInterpretation(initializer, model, cancelIndicator); | 62 | } catch (TimeoutException e) { |
81 | if (LOG.isTraceEnabled()) { | 63 | future.cancel(true); |
82 | long end = System.currentTimeMillis(); | 64 | LOG.trace("Semantics service timeout", e); |
83 | LOG.trace("Computed semantics for {} ({}) in {}ms", doc.getResourceId(), doc.getStateId(), | 65 | return new SemanticsErrorResult("Partial interpretation timed out"); |
84 | end - start); | ||
85 | } | ||
86 | return new SemanticsSuccessResult(nodeTrace, partialInterpretation); | ||
87 | } catch (RuntimeException e) { | ||
88 | LOG.debug("Error while computing semantics", e); | ||
89 | return new SemanticsErrorResult(e.getMessage()); | ||
90 | } | 66 | } |
67 | if (LOG.isTraceEnabled()) { | ||
68 | long end = System.currentTimeMillis(); | ||
69 | LOG.trace("Computed semantics for {} ({}) in {}ms", doc.getResourceId(), doc.getStateId(), | ||
70 | end - start); | ||
71 | } | ||
72 | return result; | ||
91 | } | 73 | } |
92 | 74 | ||
93 | @Nullable | 75 | @Nullable |
94 | private Problem getProblem(IXtextWebDocument doc, CancelIndicator cancelIndicator) { | 76 | private Problem getProblem(IXtextWebDocument doc, CancelIndicator cancelIndicator) { |
95 | if (!(doc instanceof XtextWebDocument webDoc)) { | 77 | if (!(doc instanceof PushWebDocument pushDoc)) { |
96 | throw new IllegalArgumentException("Unexpected IXtextWebDocument: " + doc); | 78 | throw new IllegalArgumentException("Unexpected IXtextWebDocument: " + doc); |
97 | } | 79 | } |
98 | var validationResult = webDoc.getCachedServiceResult(validationService, cancelIndicator, true); | 80 | var validationResult = pushDoc.getCachedServiceResult(validationService, cancelIndicator, true); |
99 | boolean hasError = validationResult.getIssues().stream() | 81 | boolean hasError = validationResult.getIssues().stream() |
100 | .anyMatch(issue -> "error".equals(issue.getSeverity())); | 82 | .anyMatch(issue -> "error".equals(issue.getSeverity())); |
101 | if (hasError) { | 83 | if (hasError) { |
@@ -111,53 +93,4 @@ public class SemanticsService extends AbstractCachedService<SemanticsResult> { | |||
111 | } | 93 | } |
112 | return problem; | 94 | return problem; |
113 | } | 95 | } |
114 | |||
115 | private List<String> getNodeTrace(ModelInitializer initializer) { | ||
116 | var nodeTrace = new String[initializer.getNodeCount()]; | ||
117 | for (var entry : initializer.getNodeTrace().keyValuesView()) { | ||
118 | var node = entry.getOne(); | ||
119 | var index = entry.getTwo(); | ||
120 | nodeTrace[index] = semanticsUtils.getName(node).orElse(null); | ||
121 | } | ||
122 | return Arrays.asList(nodeTrace); | ||
123 | } | ||
124 | |||
125 | private JsonObject getPartialInterpretation(ModelInitializer initializer, Model model, | ||
126 | CancelIndicator cancelIndicator) { | ||
127 | var adapter = model.getAdapter(ReasoningAdapter.class); | ||
128 | var json = new JsonObject(); | ||
129 | for (var entry : initializer.getRelationTrace().entrySet()) { | ||
130 | var relation = entry.getKey(); | ||
131 | var partialSymbol = entry.getValue(); | ||
132 | var tuples = getTuplesJson(adapter, partialSymbol); | ||
133 | var name = semanticsUtils.getName(relation).orElse(partialSymbol.name()); | ||
134 | json.add(name, tuples); | ||
135 | operationCanceledManager.checkCanceled(cancelIndicator); | ||
136 | } | ||
137 | return json; | ||
138 | } | ||
139 | |||
140 | private static JsonArray getTuplesJson(ReasoningAdapter adapter, PartialRelation partialSymbol) { | ||
141 | var interpretation = adapter.getPartialInterpretation(Concreteness.PARTIAL, partialSymbol); | ||
142 | var cursor = interpretation.getAll(); | ||
143 | var map = new TreeMap<Tuple, TruthValue>(); | ||
144 | while (cursor.move()) { | ||
145 | map.put(cursor.getKey(), cursor.getValue()); | ||
146 | } | ||
147 | var tuples = new JsonArray(); | ||
148 | for (var entry : map.entrySet()) { | ||
149 | tuples.add(toArray(entry.getKey(), entry.getValue())); | ||
150 | } | ||
151 | return tuples; | ||
152 | } | ||
153 | |||
154 | private static JsonArray toArray(Tuple tuple, TruthValue value) { | ||
155 | int arity = tuple.getSize(); | ||
156 | var json = new JsonArray(arity + 1); | ||
157 | for (int i = 0; i < arity; i++) { | ||
158 | json.add(tuple.get(i)); | ||
159 | } | ||
160 | json.add(value.toString()); | ||
161 | return json; | ||
162 | } | ||
163 | } | 96 | } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java new file mode 100644 index 00000000..25589260 --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.web.semantics; | ||
7 | |||
8 | import com.google.gson.JsonArray; | ||
9 | import com.google.gson.JsonObject; | ||
10 | import com.google.inject.Inject; | ||
11 | import org.eclipse.xtext.service.OperationCanceledManager; | ||
12 | import org.eclipse.xtext.util.CancelIndicator; | ||
13 | import org.slf4j.Logger; | ||
14 | import org.slf4j.LoggerFactory; | ||
15 | import tools.refinery.language.model.problem.Problem; | ||
16 | import tools.refinery.language.semantics.model.ModelInitializer; | ||
17 | import tools.refinery.language.semantics.model.SemanticsUtils; | ||
18 | import tools.refinery.store.model.Model; | ||
19 | import tools.refinery.store.model.ModelStore; | ||
20 | import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; | ||
21 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
22 | import tools.refinery.store.reasoning.ReasoningStoreAdapter; | ||
23 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
24 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
25 | import tools.refinery.store.representation.TruthValue; | ||
26 | import tools.refinery.store.tuple.Tuple; | ||
27 | import tools.refinery.viatra.runtime.CancellationToken; | ||
28 | |||
29 | import java.util.Arrays; | ||
30 | import java.util.List; | ||
31 | import java.util.TreeMap; | ||
32 | import java.util.concurrent.Callable; | ||
33 | |||
34 | class SemanticsWorker implements Callable<SemanticsResult> { | ||
35 | private static final Logger LOG = LoggerFactory.getLogger(SemanticsWorker.class); | ||
36 | |||
37 | @Inject | ||
38 | private SemanticsUtils semanticsUtils; | ||
39 | |||
40 | @Inject | ||
41 | private OperationCanceledManager operationCanceledManager; | ||
42 | |||
43 | @Inject | ||
44 | private ModelInitializer initializer; | ||
45 | |||
46 | private Problem problem; | ||
47 | |||
48 | private CancellationToken cancellationToken; | ||
49 | |||
50 | public void setProblem(Problem problem, CancelIndicator parentIndicator) { | ||
51 | this.problem = problem; | ||
52 | cancellationToken = () -> { | ||
53 | if (Thread.interrupted() || parentIndicator.isCanceled()) { | ||
54 | operationCanceledManager.throwOperationCanceledException(); | ||
55 | } | ||
56 | }; | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public SemanticsResult call() { | ||
61 | var builder = ModelStore.builder() | ||
62 | .with(ViatraModelQueryAdapter.builder() | ||
63 | .cancellationToken(cancellationToken)) | ||
64 | .with(ReasoningAdapter.builder() | ||
65 | .requiredInterpretations(Concreteness.PARTIAL)); | ||
66 | cancellationToken.checkCancelled(); | ||
67 | try { | ||
68 | var modelSeed = initializer.createModel(problem, builder); | ||
69 | cancellationToken.checkCancelled(); | ||
70 | var nodeTrace = getNodeTrace(initializer); | ||
71 | cancellationToken.checkCancelled(); | ||
72 | var store = builder.build(); | ||
73 | cancellationToken.checkCancelled(); | ||
74 | var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); | ||
75 | cancellationToken.checkCancelled(); | ||
76 | var partialInterpretation = getPartialInterpretation(initializer, model); | ||
77 | |||
78 | return new SemanticsSuccessResult(nodeTrace, partialInterpretation); | ||
79 | } catch (RuntimeException e) { | ||
80 | LOG.debug("Error while computing semantics", e); | ||
81 | var message = e.getMessage(); | ||
82 | return new SemanticsErrorResult(message == null ? "Partial interpretation error" : e.getMessage()); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | private List<String> getNodeTrace(ModelInitializer initializer) { | ||
87 | var nodeTrace = new String[initializer.getNodeCount()]; | ||
88 | for (var entry : initializer.getNodeTrace().keyValuesView()) { | ||
89 | var node = entry.getOne(); | ||
90 | var index = entry.getTwo(); | ||
91 | nodeTrace[index] = semanticsUtils.getName(node).orElse(null); | ||
92 | } | ||
93 | return Arrays.asList(nodeTrace); | ||
94 | } | ||
95 | |||
96 | private JsonObject getPartialInterpretation(ModelInitializer initializer, Model model) { | ||
97 | var adapter = model.getAdapter(ReasoningAdapter.class); | ||
98 | var json = new JsonObject(); | ||
99 | for (var entry : initializer.getRelationTrace().entrySet()) { | ||
100 | var relation = entry.getKey(); | ||
101 | var partialSymbol = entry.getValue(); | ||
102 | var tuples = getTuplesJson(adapter, partialSymbol); | ||
103 | var name = semanticsUtils.getName(relation).orElse(partialSymbol.name()); | ||
104 | json.add(name, tuples); | ||
105 | cancellationToken.checkCancelled(); | ||
106 | } | ||
107 | return json; | ||
108 | } | ||
109 | |||
110 | private static JsonArray getTuplesJson(ReasoningAdapter adapter, PartialRelation partialSymbol) { | ||
111 | var interpretation = adapter.getPartialInterpretation(Concreteness.PARTIAL, partialSymbol); | ||
112 | var cursor = interpretation.getAll(); | ||
113 | var map = new TreeMap<Tuple, TruthValue>(); | ||
114 | while (cursor.move()) { | ||
115 | map.put(cursor.getKey(), cursor.getValue()); | ||
116 | } | ||
117 | var tuples = new JsonArray(); | ||
118 | for (var entry : map.entrySet()) { | ||
119 | tuples.add(toArray(entry.getKey(), entry.getValue())); | ||
120 | } | ||
121 | return tuples; | ||
122 | } | ||
123 | |||
124 | private static JsonArray toArray(Tuple tuple, TruthValue value) { | ||
125 | int arity = tuple.getSize(); | ||
126 | var json = new JsonArray(arity + 1); | ||
127 | for (int i = 0; i < arity; i++) { | ||
128 | json.add(tuple.get(i)); | ||
129 | } | ||
130 | json.add(value.toString()); | ||
131 | return json; | ||
132 | } | ||
133 | } | ||
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java index 2c0e9329..74456604 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java | |||
@@ -42,6 +42,8 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener | |||
42 | 42 | ||
43 | private final List<XtextWebPushMessage> pendingPushMessages = new ArrayList<>(); | 43 | private final List<XtextWebPushMessage> pendingPushMessages = new ArrayList<>(); |
44 | 44 | ||
45 | private volatile boolean disposed; | ||
46 | |||
45 | public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { | 47 | public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { |
46 | this.session = session; | 48 | this.session = session; |
47 | this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; | 49 | this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; |
@@ -52,10 +54,13 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener | |||
52 | } | 54 | } |
53 | 55 | ||
54 | public void handleRequest(XtextWebRequest request) throws ResponseHandlerException { | 56 | public void handleRequest(XtextWebRequest request) throws ResponseHandlerException { |
57 | if (disposed) { | ||
58 | return; | ||
59 | } | ||
55 | var serviceContext = new SimpleServiceContext(session, request.getRequestData()); | 60 | var serviceContext = new SimpleServiceContext(session, request.getRequestData()); |
56 | var ping = serviceContext.getParameter("ping"); | 61 | var ping = serviceContext.getParameter("ping"); |
57 | if (ping != null) { | 62 | if (ping != null) { |
58 | responseHandler.onResponse(new XtextWebOkResponse(request, new PongResult(ping))); | 63 | onResponse(new XtextWebOkResponse(request, new PongResult(ping))); |
59 | return; | 64 | return; |
60 | } | 65 | } |
61 | synchronized (callPendingLock) { | 66 | synchronized (callPendingLock) { |
@@ -72,23 +77,36 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener | |||
72 | var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); | 77 | var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); |
73 | var service = serviceDispatcher.getService(new SubscribingServiceContext(serviceContext, this)); | 78 | var service = serviceDispatcher.getService(new SubscribingServiceContext(serviceContext, this)); |
74 | var serviceResult = service.getService().apply(); | 79 | var serviceResult = service.getService().apply(); |
75 | responseHandler.onResponse(new XtextWebOkResponse(request, serviceResult)); | 80 | onResponse(new XtextWebOkResponse(request, serviceResult)); |
76 | } catch (InvalidRequestException e) { | 81 | } catch (InvalidRequestException e) { |
77 | responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.REQUEST_ERROR, e)); | 82 | onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.REQUEST_ERROR, e)); |
78 | } catch (RuntimeException e) { | 83 | } catch (RuntimeException e) { |
79 | responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.SERVER_ERROR, e)); | 84 | onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.SERVER_ERROR, e)); |
80 | } finally { | 85 | } finally { |
81 | synchronized (callPendingLock) { | 86 | flushPendingPushMessages(); |
82 | for (var message : pendingPushMessages) { | 87 | } |
83 | try { | 88 | } |
84 | responseHandler.onResponse(message); | 89 | |
85 | } catch (ResponseHandlerException | RuntimeException e) { | 90 | private void onResponse(XtextWebResponse response) throws ResponseHandlerException { |
86 | LOG.error("Error while flushing push message", e); | 91 | if (!disposed) { |
87 | } | 92 | responseHandler.onResponse(response); |
93 | } | ||
94 | } | ||
95 | |||
96 | private void flushPendingPushMessages() { | ||
97 | synchronized (callPendingLock) { | ||
98 | for (var message : pendingPushMessages) { | ||
99 | if (disposed) { | ||
100 | return; | ||
101 | } | ||
102 | try { | ||
103 | responseHandler.onResponse(message); | ||
104 | } catch (ResponseHandlerException | RuntimeException e) { | ||
105 | LOG.error("Error while flushing push message", e); | ||
88 | } | 106 | } |
89 | pendingPushMessages.clear(); | ||
90 | callPending = false; | ||
91 | } | 107 | } |
108 | pendingPushMessages.clear(); | ||
109 | callPending = false; | ||
92 | } | 110 | } |
93 | } | 111 | } |
94 | 112 | ||
@@ -164,10 +182,12 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener | |||
164 | 182 | ||
165 | @Override | 183 | @Override |
166 | public void dispose() { | 184 | public void dispose() { |
185 | disposed = true; | ||
167 | for (var subscription : subscriptions.values()) { | 186 | for (var subscription : subscriptions.values()) { |
168 | var document = subscription.get(); | 187 | var document = subscription.get(); |
169 | if (document != null) { | 188 | if (document != null) { |
170 | document.removePrecomputationListener(this); | 189 | document.removePrecomputationListener(this); |
190 | document.cancelBackgroundWork(); | ||
171 | } | 191 | } |
172 | } | 192 | } |
173 | } | 193 | } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java index dfbd4878..1542c694 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java | |||
@@ -27,11 +27,11 @@ public class PushWebDocument extends XtextWebDocument { | |||
27 | 27 | ||
28 | private final Map<Class<?>, IServiceResult> precomputedServices = new HashMap<>(); | 28 | private final Map<Class<?>, IServiceResult> precomputedServices = new HashMap<>(); |
29 | 29 | ||
30 | private final DocumentSynchronizer synchronizer; | ||
31 | |||
30 | public PushWebDocument(String resourceId, DocumentSynchronizer synchronizer) { | 32 | public PushWebDocument(String resourceId, DocumentSynchronizer synchronizer) { |
31 | super(resourceId, synchronizer); | 33 | super(resourceId, synchronizer); |
32 | if (resourceId == null) { | 34 | this.synchronizer = synchronizer; |
33 | throw new IllegalArgumentException("resourceId must not be null"); | ||
34 | } | ||
35 | } | 35 | } |
36 | 36 | ||
37 | public void addPrecomputationListener(PrecomputationListener listener) { | 37 | public void addPrecomputationListener(PrecomputationListener listener) { |
@@ -63,6 +63,9 @@ public class PushWebDocument extends XtextWebDocument { | |||
63 | 63 | ||
64 | private <T extends IServiceResult> void notifyPrecomputationListeners(String serviceName, T result) { | 64 | private <T extends IServiceResult> void notifyPrecomputationListeners(String serviceName, T result) { |
65 | var resourceId = getResourceId(); | 65 | var resourceId = getResourceId(); |
66 | if (resourceId == null) { | ||
67 | return; | ||
68 | } | ||
66 | var stateId = getStateId(); | 69 | var stateId = getStateId(); |
67 | List<PrecomputationListener> copyOfListeners; | 70 | List<PrecomputationListener> copyOfListeners; |
68 | synchronized (precomputationListeners) { | 71 | synchronized (precomputationListeners) { |
@@ -83,4 +86,8 @@ public class PushWebDocument extends XtextWebDocument { | |||
83 | } | 86 | } |
84 | } | 87 | } |
85 | } | 88 | } |
89 | |||
90 | public void cancelBackgroundWork() { | ||
91 | synchronizer.setCanceled(true); | ||
92 | } | ||
86 | } | 93 | } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java index b6f4fb43..ec6204ef 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java | |||
@@ -27,12 +27,7 @@ public class PushWebDocumentProvider implements IWebDocumentProvider { | |||
27 | 27 | ||
28 | @Override | 28 | @Override |
29 | public XtextWebDocument get(String resourceId, IServiceContext serviceContext) { | 29 | public XtextWebDocument get(String resourceId, IServiceContext serviceContext) { |
30 | if (resourceId == null) { | 30 | return new PushWebDocument(resourceId, |
31 | return new XtextWebDocument(null, synchronizerProvider.get()); | 31 | serviceContext.getSession().get(DocumentSynchronizer.class, () -> this.synchronizerProvider.get())); |
32 | } else { | ||
33 | // We only need to send push messages if a resourceId is specified. | ||
34 | return new PushWebDocument(resourceId, | ||
35 | serviceContext.getSession().get(DocumentSynchronizer.class, () -> this.synchronizerProvider.get())); | ||
36 | } | ||
37 | } | 32 | } |
38 | } | 33 | } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java index 043d318c..923fecd6 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java | |||
@@ -70,10 +70,11 @@ public class XtextWebSocket implements ResponseHandler { | |||
70 | 70 | ||
71 | @OnWebSocketError | 71 | @OnWebSocketError |
72 | public void onError(Throwable error) { | 72 | public void onError(Throwable error) { |
73 | executor.dispose(); | ||
73 | if (webSocketSession == null) { | 74 | if (webSocketSession == null) { |
74 | return; | 75 | return; |
75 | } | 76 | } |
76 | LOG.error("Internal websocket error in connection from" + webSocketSession.getRemoteSocketAddress(), error); | 77 | LOG.error("Internal websocket error in connection from " + webSocketSession.getRemoteSocketAddress(), error); |
77 | } | 78 | } |
78 | 79 | ||
79 | @OnWebSocketMessage | 80 | @OnWebSocketMessage |
@@ -86,14 +87,18 @@ public class XtextWebSocket implements ResponseHandler { | |||
86 | try { | 87 | try { |
87 | request = gson.fromJson(reader, XtextWebRequest.class); | 88 | request = gson.fromJson(reader, XtextWebRequest.class); |
88 | } catch (JsonIOException e) { | 89 | } catch (JsonIOException e) { |
89 | LOG.error("Cannot read from websocket from" + webSocketSession.getRemoteSocketAddress(), e); | 90 | LOG.error("Cannot read from websocket from " + webSocketSession.getRemoteSocketAddress(), e); |
90 | if (webSocketSession.isOpen()) { | 91 | if (webSocketSession.isOpen()) { |
92 | executor.dispose(); | ||
91 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload", Callback.NOOP); | 93 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload", Callback.NOOP); |
92 | } | 94 | } |
93 | return; | 95 | return; |
94 | } catch (JsonParseException e) { | 96 | } catch (JsonParseException e) { |
95 | LOG.warn("Malformed websocket request from" + webSocketSession.getRemoteSocketAddress(), e); | 97 | LOG.warn("Malformed websocket request from " + webSocketSession.getRemoteSocketAddress(), e); |
96 | webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload", Callback.NOOP); | 98 | if (webSocketSession.isOpen()) { |
99 | executor.dispose(); | ||
100 | webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload", Callback.NOOP); | ||
101 | } | ||
97 | return; | 102 | return; |
98 | } | 103 | } |
99 | try { | 104 | try { |
@@ -101,6 +106,7 @@ public class XtextWebSocket implements ResponseHandler { | |||
101 | } catch (ResponseHandlerException e) { | 106 | } catch (ResponseHandlerException e) { |
102 | LOG.warn("Cannot write websocket response", e); | 107 | LOG.warn("Cannot write websocket response", e); |
103 | if (webSocketSession.isOpen()) { | 108 | if (webSocketSession.isOpen()) { |
109 | executor.dispose(); | ||
104 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write response", Callback.NOOP); | 110 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write response", Callback.NOOP); |
105 | } | 111 | } |
106 | } | 112 | } |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java index d31325f1..6b3be115 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java | |||
@@ -5,14 +5,15 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.query.viatra; | 6 | package tools.refinery.store.query.viatra; |
7 | 7 | ||
8 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; | ||
9 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
10 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
11 | import tools.refinery.store.model.ModelStore; | 8 | import tools.refinery.store.model.ModelStore; |
12 | import tools.refinery.store.query.ModelQueryBuilder; | 9 | import tools.refinery.store.query.ModelQueryBuilder; |
13 | import tools.refinery.store.query.dnf.AnyQuery; | 10 | import tools.refinery.store.query.dnf.AnyQuery; |
14 | import tools.refinery.store.query.dnf.Dnf; | 11 | import tools.refinery.store.query.dnf.Dnf; |
15 | import tools.refinery.store.query.rewriter.DnfRewriter; | 12 | import tools.refinery.store.query.rewriter.DnfRewriter; |
13 | import tools.refinery.viatra.runtime.CancellationToken; | ||
14 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
16 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
16 | 17 | ||
17 | import java.util.Collection; | 18 | import java.util.Collection; |
18 | import java.util.function.Function; | 19 | import java.util.function.Function; |
@@ -29,6 +30,8 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder { | |||
29 | 30 | ||
30 | ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); | 31 | ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); |
31 | 32 | ||
33 | ViatraModelQueryBuilder cancellationToken(CancellationToken cancellationToken); | ||
34 | |||
32 | @Override | 35 | @Override |
33 | default ViatraModelQueryBuilder queries(AnyQuery... queries) { | 36 | default ViatraModelQueryBuilder queries(AnyQuery... queries) { |
34 | ModelQueryBuilder.super.queries(queries); | 37 | ModelQueryBuilder.super.queries(queries); |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java index f1209f69..ad754988 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java | |||
@@ -18,6 +18,7 @@ import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; | |||
18 | import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher; | 18 | import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher; |
19 | import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; | 19 | import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; |
20 | import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher; | 20 | import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher; |
21 | import tools.refinery.viatra.runtime.CancellationToken; | ||
21 | import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; | 22 | import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; |
22 | import tools.refinery.viatra.runtime.api.GenericQueryGroup; | 23 | import tools.refinery.viatra.runtime.api.GenericQueryGroup; |
23 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | 24 | import tools.refinery.viatra.runtime.api.IQuerySpecification; |
@@ -81,6 +82,10 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, Mod | |||
81 | return storeAdapter; | 82 | return storeAdapter; |
82 | } | 83 | } |
83 | 84 | ||
85 | public CancellationToken getCancellationToken() { | ||
86 | return storeAdapter.getCancellationToken(); | ||
87 | } | ||
88 | |||
84 | @Override | 89 | @Override |
85 | public <T> ResultSet<T> getResultSet(Query<T> query) { | 90 | public <T> ResultSet<T> getResultSet(Query<T> query) { |
86 | var canonicalQuery = storeAdapter.getCanonicalQuery(query); | 91 | var canonicalQuery = storeAdapter.getCanonicalQuery(query); |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java index cfdc43ba..bb0630f3 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java | |||
@@ -17,6 +17,7 @@ import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; | |||
17 | import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction; | 17 | import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction; |
18 | import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; | 18 | import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; |
19 | import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; | 19 | import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; |
20 | import tools.refinery.viatra.runtime.CancellationToken; | ||
20 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | 21 | import tools.refinery.viatra.runtime.api.IQuerySpecification; |
21 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; | 22 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; |
22 | import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory; | 23 | import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory; |
@@ -35,6 +36,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via | |||
35 | // Use a cost function that ignores the initial (empty) model but allows higher arity input keys. | 36 | // Use a cost function that ignores the initial (empty) model but allows higher arity input keys. |
36 | LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction() | 37 | LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction() |
37 | ), (IQueryBackendFactory) null); | 38 | ), (IQueryBackendFactory) null); |
39 | private CancellationToken cancellationToken = CancellationToken.NONE; | ||
38 | private final CompositeRewriter rewriter; | 40 | private final CompositeRewriter rewriter; |
39 | private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery(); | 41 | private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery(); |
40 | private final Set<AnyQuery> queries = new LinkedHashSet<>(); | 42 | private final Set<AnyQuery> queries = new LinkedHashSet<>(); |
@@ -85,6 +87,12 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via | |||
85 | } | 87 | } |
86 | 88 | ||
87 | @Override | 89 | @Override |
90 | public ViatraModelQueryBuilder cancellationToken(CancellationToken cancellationToken) { | ||
91 | this.cancellationToken = cancellationToken; | ||
92 | return this; | ||
93 | } | ||
94 | |||
95 | @Override | ||
88 | public ViatraModelQueryBuilder queries(Collection<? extends AnyQuery> queries) { | 96 | public ViatraModelQueryBuilder queries(Collection<? extends AnyQuery> queries) { |
89 | checkNotConfigured(); | 97 | checkNotConfigured(); |
90 | this.queries.addAll(queries); | 98 | this.queries.addAll(queries); |
@@ -136,7 +144,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via | |||
136 | validateSymbols(store); | 144 | validateSymbols(store); |
137 | return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getSymbolViews(), | 145 | return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getSymbolViews(), |
138 | Collections.unmodifiableMap(canonicalQueryMap), Collections.unmodifiableMap(querySpecifications), | 146 | Collections.unmodifiableMap(canonicalQueryMap), Collections.unmodifiableMap(querySpecifications), |
139 | Collections.unmodifiableSet(vacuousQueries)); | 147 | Collections.unmodifiableSet(vacuousQueries), cancellationToken); |
140 | } | 148 | } |
141 | 149 | ||
142 | private ViatraQueryEngineOptions buildEngineOptions() { | 150 | private ViatraQueryEngineOptions buildEngineOptions() { |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java index 5407ad01..f32e1cc6 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java | |||
@@ -5,6 +5,7 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.query.viatra.internal; | 6 | package tools.refinery.store.query.viatra.internal; |
7 | 7 | ||
8 | import tools.refinery.viatra.runtime.CancellationToken; | ||
8 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | 9 | import tools.refinery.viatra.runtime.api.IQuerySpecification; |
9 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; | 10 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; |
10 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | 11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; |
@@ -26,18 +27,20 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd | |||
26 | private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications; | 27 | private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications; |
27 | private final Set<AnyQuery> vacuousQueries; | 28 | private final Set<AnyQuery> vacuousQueries; |
28 | private final Set<AnyQuery> allQueries; | 29 | private final Set<AnyQuery> allQueries; |
30 | private final CancellationToken cancellationToken; | ||
29 | 31 | ||
30 | ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, | 32 | ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, |
31 | Map<AnySymbolView, IInputKey> inputKeys, | 33 | Map<AnySymbolView, IInputKey> inputKeys, |
32 | Map<AnyQuery, AnyQuery> canonicalQueryMap, | 34 | Map<AnyQuery, AnyQuery> canonicalQueryMap, |
33 | Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications, | 35 | Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications, |
34 | Set<AnyQuery> vacuousQueries) { | 36 | Set<AnyQuery> vacuousQueries, CancellationToken cancellationToken) { |
35 | this.store = store; | 37 | this.store = store; |
36 | this.engineOptions = engineOptions; | 38 | this.engineOptions = engineOptions; |
37 | this.inputKeys = inputKeys; | 39 | this.inputKeys = inputKeys; |
38 | this.canonicalQueryMap = canonicalQueryMap; | 40 | this.canonicalQueryMap = canonicalQueryMap; |
39 | this.querySpecifications = querySpecifications; | 41 | this.querySpecifications = querySpecifications; |
40 | this.vacuousQueries = vacuousQueries; | 42 | this.vacuousQueries = vacuousQueries; |
43 | this.cancellationToken = cancellationToken; | ||
41 | var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size()); | 44 | var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size()); |
42 | mutableAllQueries.addAll(querySpecifications.keySet()); | 45 | mutableAllQueries.addAll(querySpecifications.keySet()); |
43 | mutableAllQueries.addAll(vacuousQueries); | 46 | mutableAllQueries.addAll(vacuousQueries); |
@@ -62,6 +65,10 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd | |||
62 | return allQueries; | 65 | return allQueries; |
63 | } | 66 | } |
64 | 67 | ||
68 | public CancellationToken getCancellationToken() { | ||
69 | return cancellationToken; | ||
70 | } | ||
71 | |||
65 | @Override | 72 | @Override |
66 | public <T> Query<T> getCanonicalQuery(Query<T> query) { | 73 | public <T> Query<T> getCanonicalQuery(Query<T> query) { |
67 | // We know that canonical forms of queries do not change output types. | 74 | // We know that canonical forms of queries do not change output types. |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java index d1fa5239..dadab5dd 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java | |||
@@ -5,6 +5,7 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.query.viatra.internal.context; | 6 | package tools.refinery.store.query.viatra.internal.context; |
7 | 7 | ||
8 | import tools.refinery.viatra.runtime.CancellationToken; | ||
8 | import tools.refinery.viatra.runtime.matchers.context.*; | 9 | import tools.refinery.viatra.runtime.matchers.context.*; |
9 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | 10 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; |
10 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | 11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; |
@@ -32,10 +33,13 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext { | |||
32 | 33 | ||
33 | private final Model model; | 34 | private final Model model; |
34 | 35 | ||
36 | private final CancellationToken cancellationToken; | ||
37 | |||
35 | RelationalRuntimeContext(ViatraModelQueryAdapterImpl adapter) { | 38 | RelationalRuntimeContext(ViatraModelQueryAdapterImpl adapter) { |
36 | model = adapter.getModel(); | 39 | model = adapter.getModel(); |
37 | metaContext = new RelationalQueryMetaContext(adapter.getStoreAdapter().getInputKeys()); | 40 | metaContext = new RelationalQueryMetaContext(adapter.getStoreAdapter().getInputKeys()); |
38 | modelUpdateListener = new ModelUpdateListener(adapter); | 41 | modelUpdateListener = new ModelUpdateListener(adapter); |
42 | cancellationToken = adapter.getCancellationToken(); | ||
39 | } | 43 | } |
40 | 44 | ||
41 | @Override | 45 | @Override |
@@ -192,4 +196,9 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext { | |||
192 | public void executeAfterTraversal(Runnable runnable) { | 196 | public void executeAfterTraversal(Runnable runnable) { |
193 | runnable.run(); | 197 | runnable.run(); |
194 | } | 198 | } |
199 | |||
200 | @Override | ||
201 | public CancellationToken getCancellationToken() { | ||
202 | return cancellationToken; | ||
203 | } | ||
195 | } | 204 | } |
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java index 16e290fd..79e0526d 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java | |||
@@ -1,26 +1,17 @@ | |||
1 | /******************************************************************************* | 1 | /******************************************************************************* |
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | 2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro |
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
3 | * This program and the accompanying materials are made available under the | 4 | * This program and the accompanying materials are made available under the |
4 | * terms of the Eclipse Public License v. 2.0 which is available at | 5 | * terms of the Eclipse Public License v. 2.0 which is available at |
5 | * http://www.eclipse.org/legal/epl-v20.html. | 6 | * http://www.eclipse.org/legal/epl-v20.html. |
6 | * | 7 | * |
7 | * SPDX-License-Identifier: EPL-2.0 | 8 | * SPDX-License-Identifier: EPL-2.0 |
8 | *******************************************************************************/ | 9 | *******************************************************************************/ |
9 | 10 | ||
10 | package tools.refinery.viatra.runtime.rete.network; | 11 | package tools.refinery.viatra.runtime.rete.network; |
11 | 12 | ||
12 | import java.util.ArrayDeque; | ||
13 | import java.util.ArrayList; | ||
14 | import java.util.Collection; | ||
15 | import java.util.Deque; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.LinkedHashSet; | ||
18 | import java.util.LinkedList; | ||
19 | import java.util.Map; | ||
20 | import java.util.Set; | ||
21 | import java.util.function.Function; | ||
22 | |||
23 | import org.apache.log4j.Logger; | 13 | import org.apache.log4j.Logger; |
14 | import tools.refinery.viatra.runtime.CancellationToken; | ||
24 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | 15 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; |
25 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | 16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; |
26 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | 17 | import tools.refinery.viatra.runtime.matchers.util.Clearable; |
@@ -42,6 +33,9 @@ import tools.refinery.viatra.runtime.rete.single.SingleInputNode; | |||
42 | import tools.refinery.viatra.runtime.rete.single.TrimmerNode; | 33 | import tools.refinery.viatra.runtime.rete.single.TrimmerNode; |
43 | import tools.refinery.viatra.runtime.rete.util.Options; | 34 | import tools.refinery.viatra.runtime.rete.util.Options; |
44 | 35 | ||
36 | import java.util.*; | ||
37 | import java.util.function.Function; | ||
38 | |||
45 | /** | 39 | /** |
46 | * @author Gabor Bergmann | 40 | * @author Gabor Bergmann |
47 | * | 41 | * |
@@ -79,6 +73,8 @@ public final class ReteContainer { | |||
79 | 73 | ||
80 | protected final TimelyConfiguration timelyConfiguration; | 74 | protected final TimelyConfiguration timelyConfiguration; |
81 | 75 | ||
76 | private final CancellationToken cancellationToken; | ||
77 | |||
82 | /** | 78 | /** |
83 | * @param threaded | 79 | * @param threaded |
84 | * false if operating in a single-threaded environment | 80 | * false if operating in a single-threaded environment |
@@ -88,6 +84,7 @@ public final class ReteContainer { | |||
88 | this.network = network; | 84 | this.network = network; |
89 | this.backendContext = network.getEngine().getBackendContext(); | 85 | this.backendContext = network.getEngine().getBackendContext(); |
90 | this.timelyConfiguration = network.getEngine().getTimelyConfiguration(); | 86 | this.timelyConfiguration = network.getEngine().getTimelyConfiguration(); |
87 | cancellationToken = backendContext.getRuntimeContext().getCancellationToken(); | ||
91 | 88 | ||
92 | this.delayedCommandQueue = new LinkedHashSet<DelayedCommand>(); | 89 | this.delayedCommandQueue = new LinkedHashSet<DelayedCommand>(); |
93 | this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>(); | 90 | this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>(); |
@@ -395,10 +392,10 @@ public final class ReteContainer { | |||
395 | 392 | ||
396 | /** | 393 | /** |
397 | * Retrieves a safe copy of the contents of a supplier. | 394 | * Retrieves a safe copy of the contents of a supplier. |
398 | * | 395 | * |
399 | * <p> Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set. | 396 | * <p> Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set. |
400 | * | 397 | * |
401 | * @param flush if true, a flush is performed before pulling the contents | 398 | * @param flush if true, a flush is performed before pulling the contents |
402 | * @since 2.3 | 399 | * @since 2.3 |
403 | */ | 400 | */ |
404 | public Collection<Tuple> pullContents(final Supplier supplier, final boolean flush) { | 401 | public Collection<Tuple> pullContents(final Supplier supplier, final boolean flush) { |
@@ -424,7 +421,7 @@ public final class ReteContainer { | |||
424 | 421 | ||
425 | /** | 422 | /** |
426 | * Retrieves the contents of a SingleInputNode's parentage. | 423 | * Retrieves the contents of a SingleInputNode's parentage. |
427 | * | 424 | * |
428 | * @since 2.3 | 425 | * @since 2.3 |
429 | */ | 426 | */ |
430 | public Collection<Tuple> pullPropagatedContents(final SingleInputNode supplier, final boolean flush) { | 427 | public Collection<Tuple> pullPropagatedContents(final SingleInputNode supplier, final boolean flush) { |
@@ -438,7 +435,7 @@ public final class ReteContainer { | |||
438 | 435 | ||
439 | /** | 436 | /** |
440 | * Retrieves the timestamp-aware contents of a SingleInputNode's parentage. | 437 | * Retrieves the timestamp-aware contents of a SingleInputNode's parentage. |
441 | * | 438 | * |
442 | * @since 2.3 | 439 | * @since 2.3 |
443 | */ | 440 | */ |
444 | public Map<Tuple, Timeline<Timestamp>> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier, | 441 | public Map<Tuple, Timeline<Timestamp>> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier, |
@@ -541,7 +538,7 @@ public final class ReteContainer { | |||
541 | 538 | ||
542 | /** | 539 | /** |
543 | * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker. | 540 | * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker. |
544 | * | 541 | * |
545 | * @since 1.6 | 542 | * @since 1.6 |
546 | */ | 543 | */ |
547 | public void deliverMessagesSingleThreaded() { | 544 | public void deliverMessagesSingleThreaded() { |
@@ -620,7 +617,7 @@ public final class ReteContainer { | |||
620 | 617 | ||
621 | /** | 618 | /** |
622 | * Returns an addressed node at this container. | 619 | * Returns an addressed node at this container. |
623 | * | 620 | * |
624 | * @pre: address.container == this, e.g. address MUST be local | 621 | * @pre: address.container == this, e.g. address MUST be local |
625 | * @throws IllegalArgumentException | 622 | * @throws IllegalArgumentException |
626 | * if address is non-local | 623 | * if address is non-local |
@@ -726,4 +723,7 @@ public final class ReteContainer { | |||
726 | return network.getInputConnector(); | 723 | return network.getInputConnector(); |
727 | } | 724 | } |
728 | 725 | ||
726 | public void checkCancelled() { | ||
727 | cancellationToken.checkCancelled(); | ||
728 | } | ||
729 | } | 729 | } |
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java index e7ec36dc..7dc7c4bc 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java | |||
@@ -1,19 +1,15 @@ | |||
1 | /******************************************************************************* | 1 | /******************************************************************************* |
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | 2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro |
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
3 | * This program and the accompanying materials are made available under the | 4 | * This program and the accompanying materials are made available under the |
4 | * terms of the Eclipse Public License v. 2.0 which is available at | 5 | * terms of the Eclipse Public License v. 2.0 which is available at |
5 | * http://www.eclipse.org/legal/epl-v20.html. | 6 | * http://www.eclipse.org/legal/epl-v20.html. |
6 | * | 7 | * |
7 | * SPDX-License-Identifier: EPL-2.0 | 8 | * SPDX-License-Identifier: EPL-2.0 |
8 | *******************************************************************************/ | 9 | *******************************************************************************/ |
9 | 10 | ||
10 | package tools.refinery.viatra.runtime.rete.network; | 11 | package tools.refinery.viatra.runtime.rete.network; |
11 | 12 | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | 13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; |
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | 14 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; |
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | 15 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; |
@@ -24,11 +20,16 @@ import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | |||
24 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | 20 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; |
25 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | 21 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; |
26 | 22 | ||
23 | import java.util.Collection; | ||
24 | import java.util.HashSet; | ||
25 | import java.util.List; | ||
26 | import java.util.Set; | ||
27 | |||
27 | /** | 28 | /** |
28 | * Base implementation for a supplier node. | 29 | * Base implementation for a supplier node. |
29 | * | 30 | * |
30 | * @author Gabor Bergmann | 31 | * @author Gabor Bergmann |
31 | * | 32 | * |
32 | */ | 33 | */ |
33 | public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode { | 34 | public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode { |
34 | protected final List<Receiver> children = CollectionsFactory.createObserverList(); | 35 | protected final List<Receiver> children = CollectionsFactory.createObserverList(); |
@@ -45,6 +46,7 @@ public abstract class StandardNode extends BaseNode implements Supplier, Network | |||
45 | * @since 2.4 | 46 | * @since 2.4 |
46 | */ | 47 | */ |
47 | protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { | 48 | protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { |
49 | reteContainer.checkCancelled(); | ||
48 | for (final Mailbox childMailbox : childMailboxes) { | 50 | for (final Mailbox childMailbox : childMailboxes) { |
49 | childMailbox.postMessage(direction, updateElement, timestamp); | 51 | childMailbox.postMessage(direction, updateElement, timestamp); |
50 | } | 52 | } |
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java new file mode 100644 index 00000000..a2ae41e3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java | |||
@@ -0,0 +1,13 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime; | ||
7 | |||
8 | @FunctionalInterface | ||
9 | public interface CancellationToken { | ||
10 | CancellationToken NONE = () -> {}; | ||
11 | |||
12 | void checkCancelled(); | ||
13 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java index c2e90614..61359c1b 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java | |||
@@ -1,82 +1,84 @@ | |||
1 | /******************************************************************************* | 1 | /******************************************************************************* |
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | 2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro |
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
3 | * This program and the accompanying materials are made available under the | 4 | * This program and the accompanying materials are made available under the |
4 | * terms of the Eclipse Public License v. 2.0 which is available at | 5 | * terms of the Eclipse Public License v. 2.0 which is available at |
5 | * http://www.eclipse.org/legal/epl-v20.html. | 6 | * http://www.eclipse.org/legal/epl-v20.html. |
6 | * | 7 | * |
7 | * SPDX-License-Identifier: EPL-2.0 | 8 | * SPDX-License-Identifier: EPL-2.0 |
8 | *******************************************************************************/ | 9 | *******************************************************************************/ |
9 | package tools.refinery.viatra.runtime.matchers.context; | 10 | package tools.refinery.viatra.runtime.matchers.context; |
10 | 11 | ||
11 | import java.lang.reflect.InvocationTargetException; | 12 | import tools.refinery.viatra.runtime.CancellationToken; |
12 | import java.util.Optional; | ||
13 | import java.util.concurrent.Callable; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; | 13 | import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; |
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | 14 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; |
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | 15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; |
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | 16 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; |
19 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | 17 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; |
20 | 18 | ||
19 | import java.lang.reflect.InvocationTargetException; | ||
20 | import java.util.Optional; | ||
21 | import java.util.concurrent.Callable; | ||
22 | |||
21 | /** | 23 | /** |
22 | * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. | 24 | * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. |
23 | * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. | 25 | * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. |
24 | * | 26 | * |
25 | * @author Bergmann Gabor | 27 | * @author Bergmann Gabor |
26 | * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. | 28 | * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. |
27 | */ | 29 | */ |
28 | public interface IQueryRuntimeContext { | 30 | public interface IQueryRuntimeContext { |
29 | /** | 31 | /** |
30 | * Provides metamodel-specific info independent of the runtime instance model. | 32 | * Provides metamodel-specific info independent of the runtime instance model. |
31 | */ | 33 | */ |
32 | public IQueryMetaContext getMetaContext(); | 34 | public IQueryMetaContext getMetaContext(); |
33 | 35 | ||
34 | 36 | ||
35 | /** | 37 | /** |
36 | * The given callable will be executed, and all model traversals will be delayed until the execution is done. If | 38 | * The given callable will be executed, and all model traversals will be delayed until the execution is done. If |
37 | * there are any outstanding information to be read from the model, a single coalesced model traversal will | 39 | * there are any outstanding information to be read from the model, a single coalesced model traversal will |
38 | * initialize the caches and deliver the notifications. | 40 | * initialize the caches and deliver the notifications. |
39 | * | 41 | * |
40 | * <p> Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. | 42 | * <p> Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. |
41 | * | 43 | * |
42 | * <p> <b>Caution: </b> results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. | 44 | * <p> <b>Caution: </b> results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. |
43 | * For example, if a certain input key is not cached yet, an empty relation may be reported during <code>callable.call()</code>; the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. | 45 | * For example, if a certain input key is not cached yet, an empty relation may be reported during <code>callable.call()</code>; the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. |
44 | * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). | 46 | * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). |
45 | * | 47 | * |
46 | * @param callable | 48 | * @param callable |
47 | */ | 49 | */ |
48 | public abstract <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException; | 50 | public abstract <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException; |
49 | /** | 51 | /** |
50 | * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). | 52 | * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). |
51 | */ | 53 | */ |
52 | public boolean isCoalescing(); | 54 | public boolean isCoalescing(); |
53 | 55 | ||
54 | /** | 56 | /** |
55 | * Returns true if index is available for the given key providing the given service. | 57 | * Returns true if index is available for the given key providing the given service. |
56 | * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 58 | * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
57 | * @since 1.4 | 59 | * @since 1.4 |
58 | */ | 60 | */ |
59 | public boolean isIndexed(IInputKey key, IndexingService service); | 61 | public boolean isIndexed(IInputKey key, IndexingService service); |
60 | 62 | ||
61 | /** | 63 | /** |
62 | * If the given (enumerable) input key is not yet indexed, the model will be traversed | 64 | * If the given (enumerable) input key is not yet indexed, the model will be traversed |
63 | * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) | 65 | * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) |
64 | * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging | 66 | * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging |
65 | * multiple indexing requests to an appropriate level. | 67 | * multiple indexing requests to an appropriate level. |
66 | * | 68 | * |
67 | * <p><b>Postcondition:</b> After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key | 69 | * <p><b>Postcondition:</b> After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key |
68 | * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. | 70 | * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. |
69 | * | 71 | * |
70 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 72 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
71 | * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 73 | * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
72 | * @since 1.4 | 74 | * @since 1.4 |
73 | */ | 75 | */ |
74 | public void ensureIndexed(IInputKey key, IndexingService service); | 76 | public void ensureIndexed(IInputKey key, IndexingService service); |
75 | 77 | ||
76 | /** | 78 | /** |
77 | * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. | 79 | * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. |
78 | * | 80 | * |
79 | * @param key an input key | 81 | * @param key an input key |
80 | * @param seedMask | 82 | * @param seedMask |
81 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | 83 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be |
82 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask. | 84 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask. |
@@ -84,59 +86,59 @@ public interface IQueryRuntimeContext { | |||
84 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | 86 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in |
85 | * parameterSeedMask, so that for each considered match tuple, | 87 | * parameterSeedMask, so that for each considered match tuple, |
86 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | 88 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. |
87 | * | 89 | * |
88 | * @return the number of tuples in the model for the given key and seed | 90 | * @return the number of tuples in the model for the given key and seed |
89 | * | 91 | * |
90 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 92 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
91 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 93 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
92 | * @since 1.7 | 94 | * @since 1.7 |
93 | */ | 95 | */ |
94 | public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); | 96 | public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); |
95 | 97 | ||
96 | 98 | ||
97 | /** | 99 | /** |
98 | * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask | 100 | * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask |
99 | * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. | 101 | * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. |
100 | * | 102 | * |
101 | * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. | 103 | * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. |
102 | * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. | 104 | * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. |
103 | * | 105 | * |
104 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | 106 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. |
105 | * | 107 | * |
106 | * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. | 108 | * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. |
107 | * | 109 | * |
108 | * @since 2.1 | 110 | * @since 2.1 |
109 | */ | 111 | */ |
110 | public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); | 112 | public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); |
111 | 113 | ||
112 | 114 | ||
113 | /** | 115 | /** |
114 | * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask | 116 | * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask |
115 | * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). | 117 | * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). |
116 | * The estimate must meet the required accuracy. | 118 | * The estimate must meet the required accuracy. |
117 | * | 119 | * |
118 | * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. | 120 | * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. |
119 | * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. | 121 | * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. |
120 | * | 122 | * |
121 | * <p> For an empty relation, zero is acceptable as an exact answer. | 123 | * <p> For an empty relation, zero is acceptable as an exact answer. |
122 | * | 124 | * |
123 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | 125 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. |
124 | * | 126 | * |
125 | * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. | 127 | * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. |
126 | * | 128 | * |
127 | * @since 2.1 | 129 | * @since 2.1 |
128 | */ | 130 | */ |
129 | public default Optional<Double> estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { | 131 | public default Optional<Double> estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { |
130 | if (key.isEnumerable()) { | 132 | if (key.isEnumerable()) { |
131 | return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, | 133 | return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, |
132 | (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); | 134 | (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); |
133 | } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); | 135 | } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); |
134 | } | 136 | } |
135 | 137 | ||
136 | 138 | ||
137 | /** | 139 | /** |
138 | * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. | 140 | * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. |
139 | * | 141 | * |
140 | * @param key an input key | 142 | * @param key an input key |
141 | * @param seedMask | 143 | * @param seedMask |
142 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | 144 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be |
@@ -144,23 +146,23 @@ public interface IQueryRuntimeContext { | |||
144 | * @param seed | 146 | * @param seed |
145 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | 147 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in |
146 | * parameterSeedMask, so that for each considered match tuple, | 148 | * parameterSeedMask, so that for each considered match tuple, |
147 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | 149 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. |
148 | * @return the tuples in the model for the given key and seed | 150 | * @return the tuples in the model for the given key and seed |
149 | * | 151 | * |
150 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 152 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
151 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 153 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
152 | * @since 1.7 | 154 | * @since 1.7 |
153 | */ | 155 | */ |
154 | public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); | 156 | public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); |
155 | 157 | ||
156 | /** | 158 | /** |
157 | * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples | 159 | * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples |
158 | * are bound by the seed except for one. | 160 | * are bound by the seed except for one. |
159 | * | 161 | * |
160 | * <p> | 162 | * <p> |
161 | * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given | 163 | * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given |
162 | * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. | 164 | * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. |
163 | * | 165 | * |
164 | * @param key | 166 | * @param key |
165 | * an input key | 167 | * an input key |
166 | * @param seedMask | 168 | * @param seedMask |
@@ -172,7 +174,7 @@ public interface IQueryRuntimeContext { | |||
172 | * parameterSeedMask, so that for each considered match tuple, | 174 | * parameterSeedMask, so that for each considered match tuple, |
173 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | 175 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. |
174 | * @return the objects in the model for the given key and seed | 176 | * @return the objects in the model for the given key and seed |
175 | * | 177 | * |
176 | * <p> | 178 | * <p> |
177 | * <b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 179 | * <b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
178 | * @throws IllegalArgumentException | 180 | * @throws IllegalArgumentException |
@@ -180,17 +182,17 @@ public interface IQueryRuntimeContext { | |||
180 | * @since 1.7 | 182 | * @since 1.7 |
181 | */ | 183 | */ |
182 | public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); | 184 | public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); |
183 | 185 | ||
184 | /** | 186 | /** |
185 | * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples | 187 | * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples |
186 | * are bound by the seed. | 188 | * are bound by the seed. |
187 | * | 189 | * |
188 | * <p> | 190 | * <p> |
189 | * Returns whether the given tuple is in the extensional relation identified by the input key. | 191 | * Returns whether the given tuple is in the extensional relation identified by the input key. |
190 | * | 192 | * |
191 | * <p> | 193 | * <p> |
192 | * Note: this call works for non-enumerable input keys as well. | 194 | * Note: this call works for non-enumerable input keys as well. |
193 | * | 195 | * |
194 | * @param key | 196 | * @param key |
195 | * an input key | 197 | * an input key |
196 | * @param seed | 198 | * @param seed |
@@ -202,31 +204,31 @@ public interface IQueryRuntimeContext { | |||
202 | */ | 204 | */ |
203 | public boolean containsTuple(IInputKey key, ITuple seed); | 205 | public boolean containsTuple(IInputKey key, ITuple seed); |
204 | 206 | ||
205 | 207 | ||
206 | /** | 208 | /** |
207 | * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. | 209 | * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. |
208 | * <p> This should be called after invoking | 210 | * <p> This should be called after invoking |
209 | * | 211 | * |
210 | * @param key an input key | 212 | * @param key an input key |
211 | * @param seed can be null or a tuple with matching arity; | 213 | * @param seed can be null or a tuple with matching arity; |
212 | * if non-null, only those updates in the model are notified about | 214 | * if non-null, only those updates in the model are notified about |
213 | * that match the seed at positions where the seed is non-null. | 215 | * that match the seed at positions where the seed is non-null. |
214 | * @param listener will be notified of future changes | 216 | * @param listener will be notified of future changes |
215 | * | 217 | * |
216 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 218 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
217 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 219 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
218 | */ | 220 | */ |
219 | public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); | 221 | public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); |
220 | 222 | ||
221 | /** | 223 | /** |
222 | * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. | 224 | * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. |
223 | * | 225 | * |
224 | * @param key an input key | 226 | * @param key an input key |
225 | * @param seed can be null or a tuple with matching arity; | 227 | * @param seed can be null or a tuple with matching arity; |
226 | * if non-null, only those updates in the model are notified about | 228 | * if non-null, only those updates in the model are notified about |
227 | * that match the seed at positions where the seed is non-null. | 229 | * that match the seed at positions where the seed is non-null. |
228 | * @param listener will no longer be notified of future changes | 230 | * @param listener will no longer be notified of future changes |
229 | * | 231 | * |
230 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 232 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
231 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | 233 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. |
232 | */ | 234 | */ |
@@ -234,16 +236,16 @@ public interface IQueryRuntimeContext { | |||
234 | /* | 236 | /* |
235 | TODO: uniqueness | 237 | TODO: uniqueness |
236 | */ | 238 | */ |
237 | 239 | ||
238 | /** | 240 | /** |
239 | * Wraps the external element into the internal representation that is to be used by the query backend | 241 | * Wraps the external element into the internal representation that is to be used by the query backend |
240 | * <p> model element -> internal object. | 242 | * <p> model element -> internal object. |
241 | * <p> null must be mapped to null. | 243 | * <p> null must be mapped to null. |
242 | */ | 244 | */ |
243 | public Object wrapElement(Object externalElement); | 245 | public Object wrapElement(Object externalElement); |
244 | 246 | ||
245 | /** | 247 | /** |
246 | * Unwraps the internal representation of the element into its original form | 248 | * Unwraps the internal representation of the element into its original form |
247 | * <p> internal object -> model element | 249 | * <p> internal object -> model element |
248 | * <p> null must be mapped to null. | 250 | * <p> null must be mapped to null. |
249 | */ | 251 | */ |
@@ -269,13 +271,17 @@ public interface IQueryRuntimeContext { | |||
269 | * @since 1.4 | 271 | * @since 1.4 |
270 | */ | 272 | */ |
271 | public void ensureWildcardIndexing(IndexingService service); | 273 | public void ensureWildcardIndexing(IndexingService service); |
272 | 274 | ||
273 | /** | 275 | /** |
274 | * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as | 276 | * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as |
275 | * the indexing is finished. The callback is executed only once, then is removed from the callback queue. | 277 | * the indexing is finished. The callback is executed only once, then is removed from the callback queue. |
276 | * @param traversalCallback | 278 | * @param traversalCallback |
277 | * @throws InvocationTargetException | 279 | * @throws InvocationTargetException |
278 | * @since 1.4 | 280 | * @since 1.4 |
279 | */ | 281 | */ |
280 | public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; | 282 | public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; |
283 | |||
284 | default CancellationToken getCancellationToken() { | ||
285 | return CancellationToken.NONE; | ||
286 | } | ||
281 | } | 287 | } |