diff options
Diffstat (limited to 'subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java')
-rw-r--r-- | subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java new file mode 100644 index 00000000..9121fc44 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java | |||
@@ -0,0 +1,346 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
3 | * 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 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.rete.network; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
17 | import tools.refinery.viatra.runtime.rete.boundary.InputConnector; | ||
18 | import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper; | ||
19 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
20 | import tools.refinery.viatra.runtime.rete.index.OnetimeIndexer; | ||
21 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand; | ||
23 | import tools.refinery.viatra.runtime.rete.recipes.*; | ||
24 | import tools.refinery.viatra.runtime.rete.recipes.helper.RecipeRecognizer; | ||
25 | import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper; | ||
26 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
27 | import tools.refinery.viatra.runtime.rete.remote.RemoteReceiver; | ||
28 | import tools.refinery.viatra.runtime.rete.remote.RemoteSupplier; | ||
29 | import tools.refinery.viatra.runtime.rete.traceability.ActiveNodeConflictTrace; | ||
30 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
31 | import tools.refinery.viatra.runtime.rete.traceability.UserRequestTrace; | ||
32 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
33 | |||
34 | import java.util.Map; | ||
35 | import java.util.Set; | ||
36 | |||
37 | /** | ||
38 | * Stores the internal parts of a rete network. Nodes are stored according to type and parameters. | ||
39 | * | ||
40 | * @author Gabor Bergmann | ||
41 | */ | ||
42 | public class NodeProvisioner { | ||
43 | |||
44 | // boolean activeStorage = true; | ||
45 | |||
46 | ReteContainer reteContainer; | ||
47 | NodeFactory nodeFactory; | ||
48 | ConnectionFactory connectionFactory; | ||
49 | InputConnector inputConnector; | ||
50 | IQueryRuntimeContext runtimeContext; | ||
51 | |||
52 | // TODO as recipe? | ||
53 | Map<Supplier, RemoteReceiver> remoteReceivers = CollectionsFactory.createMap(); | ||
54 | Map<Address<? extends Supplier>, RemoteSupplier> remoteSuppliers = CollectionsFactory.createMap(); | ||
55 | |||
56 | private RecipeRecognizer recognizer; | ||
57 | |||
58 | /** | ||
59 | * PRE: NodeFactory, ConnectionFactory must exist | ||
60 | * | ||
61 | * @param reteContainer | ||
62 | * the ReteNet whose interior is to be mapped. | ||
63 | */ | ||
64 | public NodeProvisioner(ReteContainer reteContainer) { | ||
65 | super(); | ||
66 | this.reteContainer = reteContainer; | ||
67 | this.nodeFactory = reteContainer.getNodeFactory(); | ||
68 | this.connectionFactory = reteContainer.getConnectionFactory(); | ||
69 | this.inputConnector = reteContainer.getInputConnectionFactory(); | ||
70 | runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext(); | ||
71 | recognizer = new RecipeRecognizer(runtimeContext); | ||
72 | } | ||
73 | |||
74 | public synchronized Address<? extends Node> getOrCreateNodeByRecipe(RecipeTraceInfo recipeTrace) { | ||
75 | ReteNodeRecipe recipe = recipeTrace.getRecipe(); | ||
76 | Address<? extends Node> result = getNodesByRecipe().get(recipe); | ||
77 | if (result != null) { | ||
78 | // NODE ALREADY CONSTRUCTED FOR RECIPE, only needs to add trace | ||
79 | if (getRecipeTraces().add(recipeTrace)) | ||
80 | result.getNodeCache().assignTraceInfo(recipeTrace); | ||
81 | } else { | ||
82 | // No node for this recipe object - but equivalent recipes still | ||
83 | // reusable | ||
84 | ReteNodeRecipe canonicalRecipe = recognizer.canonicalizeRecipe(recipe); | ||
85 | if (canonicalRecipe != recipe) { | ||
86 | // FOUND EQUIVALENT RECIPE | ||
87 | result = getNodesByRecipe().get(canonicalRecipe); | ||
88 | if (result != null) { | ||
89 | // NODE ALREADY CONSTRUCTED FOR EQUIVALENT RECIPE | ||
90 | recipeTrace.shadowWithEquivalentRecipe(canonicalRecipe); | ||
91 | getNodesByRecipe().put(recipe, result); | ||
92 | if (getRecipeTraces().add(recipeTrace)) | ||
93 | result.getNodeCache().assignTraceInfo(recipeTrace); | ||
94 | // Bug 491922: ensure that recipe shadowing propagates to | ||
95 | // parent traces | ||
96 | // note that if equivalentRecipes() becomes more | ||
97 | // sophisticated | ||
98 | // and considers recipes with different parents, this might | ||
99 | // have to be changed | ||
100 | ensureParents(recipeTrace); | ||
101 | } else { | ||
102 | // CONSTRUCTION IN PROGRESS FOR EQUIVALENT RECIPE | ||
103 | if (recipe instanceof IndexerRecipe) { | ||
104 | // this is allowed for indexers; | ||
105 | // go on with the construction, as the same indexer node | ||
106 | // will be obtained anyways | ||
107 | } else { | ||
108 | throw new IllegalStateException( | ||
109 | "This should not happen: " + "non-indexer nodes are are supposed to be constructed " | ||
110 | + "as soon as they are designated as canonical recipes"); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | if (result == null) { | ||
115 | // MUST INSTANTIATE NEW NODE FOR RECIPE | ||
116 | final Node freshNode = instantiateNodeForRecipe(recipeTrace, recipe); | ||
117 | result = reteContainer.makeAddress(freshNode); | ||
118 | } | ||
119 | } | ||
120 | return result; | ||
121 | } | ||
122 | |||
123 | private Set<RecipeTraceInfo> getRecipeTraces() { | ||
124 | return reteContainer.network.recipeTraces; | ||
125 | } | ||
126 | |||
127 | private Node instantiateNodeForRecipe(RecipeTraceInfo recipeTrace, final ReteNodeRecipe recipe) { | ||
128 | this.getRecipeTraces().add(recipeTrace); | ||
129 | if (recipe instanceof IndexerRecipe) { | ||
130 | |||
131 | // INSTANTIATE AND HOOK UP | ||
132 | // (cannot delay hooking up, because parent determines indexer | ||
133 | // implementation) | ||
134 | ensureParents(recipeTrace); | ||
135 | final ReteNodeRecipe parentRecipe = recipeTrace.getParentRecipeTraces().iterator().next().getRecipe(); | ||
136 | final Indexer result = nodeFactory.createIndexer(reteContainer, (IndexerRecipe) recipe, | ||
137 | asSupplier( | ||
138 | (Address<? extends Supplier>) reteContainer.network.getExistingNodeByRecipe(parentRecipe)), | ||
139 | recipeTrace); | ||
140 | |||
141 | // REMEMBER | ||
142 | if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) { | ||
143 | getNodesByRecipe().put(recipe, reteContainer.makeAddress(result)); | ||
144 | } | ||
145 | |||
146 | return result; | ||
147 | } else { | ||
148 | |||
149 | // INSTANTIATE | ||
150 | Node result = nodeFactory.createNode(reteContainer, recipe, recipeTrace); | ||
151 | |||
152 | // REMEMBER | ||
153 | if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) { | ||
154 | getNodesByRecipe().put(recipe, reteContainer.makeAddress(result)); | ||
155 | } | ||
156 | |||
157 | // HOOK UP | ||
158 | // (recursion-tolerant due to this delayed order of initialization) | ||
159 | if (recipe instanceof InputRecipe) { | ||
160 | inputConnector.connectInput((InputRecipe) recipe, result); | ||
161 | } else { | ||
162 | if (recipe instanceof InputFilterRecipe) | ||
163 | inputConnector.connectInputFilter((InputFilterRecipe) recipe, result); | ||
164 | ensureParents(recipeTrace); | ||
165 | connectionFactory.connectToParents(recipeTrace, result); | ||
166 | } | ||
167 | return result; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | private Map<ReteNodeRecipe, Address<? extends Node>> getNodesByRecipe() { | ||
172 | return reteContainer.network.nodesByRecipe; | ||
173 | } | ||
174 | |||
175 | private void ensureParents(RecipeTraceInfo recipeTrace) { | ||
176 | for (RecipeTraceInfo parentTrace : recipeTrace.getParentRecipeTraces()) { | ||
177 | getOrCreateNodeByRecipe(parentTrace); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | //// Remoting - TODO eliminate? | ||
182 | |||
183 | synchronized RemoteReceiver accessRemoteReceiver(Address<? extends Supplier> address) { | ||
184 | throw new UnsupportedOperationException("Multi-container Rete not supported yet"); | ||
185 | // if (!reteContainer.isLocal(address)) | ||
186 | // return | ||
187 | // address.getContainer().getProvisioner().accessRemoteReceiver(address); | ||
188 | // Supplier localSupplier = reteContainer.resolveLocal(address); | ||
189 | // RemoteReceiver result = remoteReceivers.get(localSupplier); | ||
190 | // if (result == null) { | ||
191 | // result = new RemoteReceiver(reteContainer); | ||
192 | // reteContainer.connect(localSupplier, result); // stateless node, no | ||
193 | // // synch required | ||
194 | // | ||
195 | // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) | ||
196 | // remoteReceivers.put(localSupplier, result); | ||
197 | // } | ||
198 | // return result; | ||
199 | } | ||
200 | |||
201 | /** | ||
202 | * @pre: address is NOT local | ||
203 | */ | ||
204 | synchronized RemoteSupplier accessRemoteSupplier(Address<? extends Supplier> address) { | ||
205 | throw new UnsupportedOperationException("Multi-container Rete not supported yet"); | ||
206 | // RemoteSupplier result = remoteSuppliers.get(address); | ||
207 | // if (result == null) { | ||
208 | // result = new RemoteSupplier(reteContainer, | ||
209 | // address.getContainer().getProvisioner() | ||
210 | // .accessRemoteReceiver(address)); | ||
211 | // // network.connectAndSynchronize(supplier, result); | ||
212 | // | ||
213 | // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) | ||
214 | // remoteSuppliers.put(address, result); | ||
215 | // } | ||
216 | // return result; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * The powerful method for accessing any (supplier) Address as a local supplier. | ||
221 | */ | ||
222 | public Supplier asSupplier(Address<? extends Supplier> address) { | ||
223 | if (!reteContainer.isLocal(address)) | ||
224 | return accessRemoteSupplier(address); | ||
225 | else | ||
226 | return reteContainer.resolveLocal(address); | ||
227 | } | ||
228 | |||
229 | /** the composite key tuple is formed as (RecipeTraceInfo, TupleMask) */ | ||
230 | private Map<Tuple, UserRequestTrace> projectionIndexerUserRequests = CollectionsFactory.createMap(); | ||
231 | |||
232 | // local version | ||
233 | // TODO remove? | ||
234 | public synchronized ProjectionIndexer accessProjectionIndexer(RecipeTraceInfo productionTrace, TupleMask mask) { | ||
235 | Tuple tableKey = Tuples.staticArityFlatTupleOf(productionTrace, mask); | ||
236 | UserRequestTrace indexerTrace = projectionIndexerUserRequests.computeIfAbsent(tableKey, k -> { | ||
237 | final ProjectionIndexerRecipe projectionIndexerRecipe = projectionIndexerRecipe( | ||
238 | productionTrace, mask); | ||
239 | return new UserRequestTrace(projectionIndexerRecipe, productionTrace); | ||
240 | }); | ||
241 | final Address<? extends Node> address = getOrCreateNodeByRecipe(indexerTrace); | ||
242 | return (ProjectionIndexer) reteContainer.resolveLocal(address); | ||
243 | } | ||
244 | |||
245 | // local version | ||
246 | public synchronized ProjectionIndexer accessProjectionIndexerOnetime(RecipeTraceInfo supplierTrace, | ||
247 | TupleMask mask) { | ||
248 | if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) | ||
249 | return accessProjectionIndexer(supplierTrace, mask); | ||
250 | |||
251 | final Address<? extends Node> supplierAddress = getOrCreateNodeByRecipe(supplierTrace); | ||
252 | Supplier supplier = (Supplier) reteContainer.resolveLocal(supplierAddress); | ||
253 | |||
254 | OnetimeIndexer result = new OnetimeIndexer(reteContainer, mask); | ||
255 | reteContainer.getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, result, reteContainer)); | ||
256 | |||
257 | return result; | ||
258 | } | ||
259 | |||
260 | // local, read-only version | ||
261 | public synchronized ProjectionIndexer peekProjectionIndexer(RecipeTraceInfo supplierTrace, TupleMask mask) { | ||
262 | final Address<? extends Node> address = getNodesByRecipe().get(projectionIndexerRecipe(supplierTrace, mask)); | ||
263 | return address == null ? null : (ProjectionIndexer) reteContainer.resolveLocal(address); | ||
264 | } | ||
265 | |||
266 | private ProjectionIndexerRecipe projectionIndexerRecipe( | ||
267 | RecipeTraceInfo parentTrace, TupleMask mask) { | ||
268 | final ReteNodeRecipe parentRecipe = parentTrace.getRecipe(); | ||
269 | Tuple tableKey = Tuples.staticArityFlatTupleOf(parentRecipe, mask); | ||
270 | ProjectionIndexerRecipe projectionIndexerRecipe = resultSeedRecipes.computeIfAbsent(tableKey, k -> | ||
271 | RecipesHelper.projectionIndexerRecipe(parentRecipe, CompilerHelper.toRecipeMask(mask)) | ||
272 | ); | ||
273 | return projectionIndexerRecipe; | ||
274 | } | ||
275 | |||
276 | /** the composite key tuple is formed as (ReteNodeRecipe, TupleMask) */ | ||
277 | private Map<Tuple, ProjectionIndexerRecipe> resultSeedRecipes = CollectionsFactory.createMap(); | ||
278 | |||
279 | // public synchronized Address<? extends Supplier> | ||
280 | // accessValueBinderFilterNode( | ||
281 | // Address<? extends Supplier> supplierAddress, int bindingIndex, Object | ||
282 | // bindingValue) { | ||
283 | // Supplier supplier = asSupplier(supplierAddress); | ||
284 | // Object[] paramsArray = { supplier.getNodeId(), bindingIndex, bindingValue | ||
285 | // }; | ||
286 | // Tuple params = new FlatTuple(paramsArray); | ||
287 | // ValueBinderFilterNode result = valueBinderFilters.get(params); | ||
288 | // if (result == null) { | ||
289 | // result = new ValueBinderFilterNode(reteContainer, bindingIndex, | ||
290 | // bindingValue); | ||
291 | // reteContainer.connect(supplier, result); // stateless node, no synch | ||
292 | // // required | ||
293 | // | ||
294 | // if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) | ||
295 | // valueBinderFilters.put(params, result); | ||
296 | // } | ||
297 | // return reteContainer.makeAddress(result); | ||
298 | // } | ||
299 | |||
300 | /** | ||
301 | * Returns a copy of the given indexer that is an active node by itself (created if does not exist). (Convention: | ||
302 | * attached with same mask to a transparent node that is attached to parent node.) Node is created if it does not | ||
303 | * exist yet. | ||
304 | * | ||
305 | * @return an identical but active indexer | ||
306 | */ | ||
307 | // TODO rethink traceability | ||
308 | RecipeTraceInfo accessActiveIndexer(RecipeTraceInfo inactiveIndexerRecipeTrace) { | ||
309 | final RecipeTraceInfo parentRecipeTrace = inactiveIndexerRecipeTrace.getParentRecipeTraces().iterator().next(); | ||
310 | final ProjectionIndexerRecipe inactiveIndexerRecipe = (ProjectionIndexerRecipe) inactiveIndexerRecipeTrace | ||
311 | .getRecipe(); | ||
312 | |||
313 | final TransparentRecipe transparentRecipe = RecipesFactory.eINSTANCE.createTransparentRecipe(); | ||
314 | transparentRecipe.setParent(parentRecipeTrace.getRecipe()); | ||
315 | final ActiveNodeConflictTrace transparentRecipeTrace = new ActiveNodeConflictTrace(transparentRecipe, | ||
316 | parentRecipeTrace, inactiveIndexerRecipeTrace); | ||
317 | |||
318 | final ProjectionIndexerRecipe activeIndexerRecipe = RecipesFactory.eINSTANCE | ||
319 | .createProjectionIndexerRecipe(); | ||
320 | activeIndexerRecipe.setParent(transparentRecipe); | ||
321 | activeIndexerRecipe.setMask(inactiveIndexerRecipe.getMask()); | ||
322 | final ActiveNodeConflictTrace activeIndexerRecipeTrace = new ActiveNodeConflictTrace(activeIndexerRecipe, | ||
323 | transparentRecipeTrace, inactiveIndexerRecipeTrace); | ||
324 | |||
325 | return activeIndexerRecipeTrace; | ||
326 | } | ||
327 | |||
328 | // /** | ||
329 | // * @param parent | ||
330 | // * @return | ||
331 | // */ | ||
332 | // private TransparentNode accessTransparentNodeInternal(Supplier parent) { | ||
333 | // nodeFactory. | ||
334 | // return null; | ||
335 | // } | ||
336 | |||
337 | // public synchronized void registerSpecializedProjectionIndexer(Node node, | ||
338 | // ProjectionIndexer indexer) { | ||
339 | // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) { | ||
340 | // Object[] paramsArray = { node.getNodeId(), indexer.getMask() }; | ||
341 | // Tuple params = new FlatTuple(paramsArray); | ||
342 | // projectionIndexers.put(params, indexer); | ||
343 | // } | ||
344 | // } | ||
345 | |||
346 | } | ||