1 package org.simantics.scl.db;
3 import java.io.IOException;
4 import java.util.ArrayList;
7 import org.cojen.classfile.TypeDesc;
8 import org.simantics.Simantics;
9 import org.simantics.db.ReadGraph;
10 import org.simantics.db.Resource;
11 import org.simantics.db.VirtualGraph;
12 import org.simantics.db.WriteGraph;
13 import org.simantics.db.common.procedure.adapter.SyncListenerAdapter;
14 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
15 import org.simantics.db.common.request.BinaryRead;
16 import org.simantics.db.common.request.DelayedWriteRequest;
17 import org.simantics.db.common.request.ReadRequest;
18 import org.simantics.db.common.request.UnaryRead;
19 import org.simantics.db.common.request.WriteRequest;
20 import org.simantics.db.common.request.WriteResultRequest;
21 import org.simantics.db.exception.DatabaseException;
22 import org.simantics.db.layer0.util.Layer0Utils;
23 import org.simantics.db.layer0.variable.Variables;
24 import org.simantics.db.request.Read;
25 import org.simantics.db.service.ClusterControl;
26 import org.simantics.db.service.QueryControl;
27 import org.simantics.db.service.SerialisationSupport;
28 import org.simantics.db.service.VirtualGraphSupport;
29 import org.simantics.layer0.utils.triggers.IActivationManager;
30 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
31 import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification;
32 import org.simantics.scl.compiler.errors.DoesNotExist;
33 import org.simantics.scl.compiler.errors.Failable;
34 import org.simantics.scl.compiler.errors.Failure;
35 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
36 import org.simantics.scl.compiler.module.Module;
37 import org.simantics.scl.compiler.module.repository.ImportFailureException;
38 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
39 import org.simantics.scl.compiler.runtime.RuntimeModule;
40 import org.simantics.scl.compiler.top.ValueNotFound;
41 import org.simantics.scl.compiler.types.TCon;
42 import org.simantics.scl.compiler.types.Type;
43 import org.simantics.scl.compiler.types.Types;
44 import org.simantics.scl.compiler.types.exceptions.MatchException;
45 import org.simantics.scl.compiler.types.util.MultiFunction;
46 import org.simantics.scl.osgi.SCLOsgi;
47 import org.simantics.scl.reflection.ValueNotFoundException;
48 import org.simantics.scl.runtime.SCLContext;
49 import org.simantics.scl.runtime.function.Function;
50 import org.simantics.scl.runtime.function.Function1;
51 import org.simantics.scl.runtime.reporting.SCLReportingHandler;
52 import org.simantics.scl.runtime.tuple.Tuple;
53 import org.simantics.scl.runtime.tuple.Tuple0;
54 import org.simantics.utils.DataContainer;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
58 @SuppressWarnings({"rawtypes", "unchecked"})
59 public class SCLFunctions {
61 private static final Logger LOGGER = LoggerFactory.getLogger(SCLFunctions.class);
63 public static final String GRAPH = "graph";
65 public static <T> T safeExec(final Function f) {
67 return (T)f.apply(Tuple0.INSTANCE);
68 } catch (Throwable t) {
69 LOGGER.error("safeExec caught exception", t);
74 public static Function resolveFunction(RuntimeModule rm, String function) throws ValueNotFound {
75 return (Function)rm.getValue(function);
78 private static SCLValue resolveSCLValue(RuntimeModule rm, String function) throws ValueNotFound {
79 return rm.getModule().getValue(function);
82 private static RuntimeModule resolveRuntimeModule(String module) throws ValueNotFound {
83 Failable<RuntimeModule> f = SCLOsgi.MODULE_REPOSITORY.getRuntimeModule(module);
86 else if(f == DoesNotExist.INSTANCE)
87 throw new ValueNotFound("Didn't find module " + module);
89 throw new ValueNotFound(((Failure)f).toString());
92 private static List<TCon> getEffects(SCLValue value) throws ValueNotFoundException, ValueNotFound, MatchException {
94 Type type = value.getType();
95 MultiFunction mfun = Types.matchFunction(type, 1);
96 ArrayList<TCon> concreteEffects = new ArrayList<>();
97 mfun.effect.collectConcreteEffects(concreteEffects);
98 return concreteEffects;
102 public static List<TCon> getEffects(RuntimeModule rm, String function) throws ValueNotFoundException, ValueNotFound, MatchException {
103 return getEffects(resolveSCLValue(rm, function));
106 public static List<TCon> getEffects(String module, String function) throws ValueNotFoundException, ValueNotFound, MatchException {
107 return getEffects(resolveSCLValue(resolveRuntimeModule(module), function));
110 private static <T> T evaluate(Function function, Object ... args) {
111 return (T)function.applyArray(args);
114 private static <T> T evaluate(RuntimeModule rm, String function, Object ... args) throws ValueNotFound {
115 return evaluate(resolveFunction(rm, function));
118 public static <T> T evaluate(String module, String function, Object ... args) throws ValueNotFound {
119 return evaluate(resolveRuntimeModule(module), function, args);
122 public static <T> T evaluateDB(String module, String function, Object ... args) throws DatabaseException {
124 RuntimeModule rm = resolveRuntimeModule(module);
125 List<TCon> effects = getEffects(resolveSCLValue(rm, function));
126 Function f = resolveFunction(rm, function);
127 if(effects.contains(Types.WRITE_GRAPH)) {
128 return syncWrite(f, args);
129 } else if(effects.contains(Types.READ_GRAPH)) {
130 return syncRead(f, args);
132 return evaluate(f, args);
134 } catch (ValueNotFound e) {
135 throw new DatabaseException("SCL Value not found: " + e.name);
136 } catch (Throwable t) {
137 if (t instanceof DatabaseException)
138 throw (DatabaseException) t;
139 throw new DatabaseException(t);
143 public static <T> T evaluateGraph(String module, String function, Object graph, Object ... args) throws DatabaseException {
144 final SCLContext context = SCLContext.getCurrent();
145 SCLContext.push(context);
146 Object oldGraph = context.put(GRAPH, graph);
148 return evaluateDB(module, function, args);
150 context.put(GRAPH, oldGraph);
155 private static Object[] NO_ARGS = new Object[] { Tuple0.INSTANCE };
157 public static <T> void asyncRead(final Function f) throws DatabaseException {
158 asyncRead(f, NO_ARGS);
161 public static void asyncRead(final Function f, final Object ... args) throws DatabaseException {
162 final SCLContext context = SCLContext.createDerivedContext();
163 Simantics.getSession().asyncRequest(new ReadRequest() {
165 public void run(ReadGraph graph) throws DatabaseException {
166 SCLContext.push(context);
167 context.put(GRAPH, graph);
169 f.apply(Tuple0.INSTANCE);
177 public static <T> T syncRead(final Function f) throws DatabaseException {
178 return syncRead(f, NO_ARGS);
181 public static <T> T syncRead(final Function f, final Object ... args) throws DatabaseException {
182 final SCLContext context = SCLContext.getCurrent();
183 Object graph = context.get(GRAPH);
185 return (T)f.applyArray(args);
187 return Simantics.getSession().syncRequest(new Read<T>() {
189 public T perform(ReadGraph graph) throws DatabaseException {
190 SCLContext.push(context);
191 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
193 return (T)f.apply(Tuple0.INSTANCE);
195 context.put(GRAPH, oldGraph);
203 public static void asyncWrite(final Function f) throws DatabaseException {
204 asyncWrite(f, NO_ARGS);
207 public static void asyncWrite(final Function f, final Object ... args) throws DatabaseException {
208 SCLContext context = SCLContext.createDerivedContext();
209 if (Simantics.peekSession() != null) {
210 Simantics.getSession().asyncRequest(new WriteRequest() {
212 public void perform(WriteGraph graph) throws DatabaseException {
213 SCLContext.push(context);
214 context.put(GRAPH, graph);
223 LOGGER.warn("No session available for asynchronous write requests");
227 public static <T> T syncWrite(final Function f) throws DatabaseException {
228 return syncWrite(f, NO_ARGS);
231 public static <T> T syncWrite(final Function f, final Object ... args) throws DatabaseException {
232 final SCLContext context = SCLContext.getCurrent();
233 Object graph = context.get(GRAPH);
235 return (T)f.apply(Tuple0.INSTANCE);
237 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
238 return Simantics.getSession().syncRequest(new WriteResultRequest<T>() {
240 public T perform(WriteGraph graph) throws DatabaseException {
241 SCLContext.push(context);
242 SCLReportingHandler oldPrinter = (SCLReportingHandler)context.put(SCLReportingHandler.REPORTING_HANDLER, printer);
243 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
245 return (T)f.apply(args);
247 context.put(GRAPH, oldGraph);
248 context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
256 public static <T> T delayedSyncWrite(final Function f) throws DatabaseException {
257 final SCLContext context = SCLContext.getCurrent();
258 final DataContainer<T> dc = new DataContainer<T>(null);
260 DelayedWriteRequest request = new DelayedWriteRequest() {
262 public void perform(WriteGraph graph) throws DatabaseException {
263 final SCLContext context = SCLContext.getCurrent();
264 SCLContext.push(context);
265 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
267 dc.set((T)f.apply(Tuple0.INSTANCE));
269 context.put(GRAPH, oldGraph);
275 Object graph = context.get(GRAPH);
277 if (graph instanceof WriteGraph) {
278 ((WriteGraph)graph).syncRequest(request);
280 throw new DatabaseException("Caller is inside a read transaction.");
283 Simantics.getSession().syncRequest(request);
288 public static <T> T virtualSyncWriteMem(WriteGraph graph, String virtualGraphId, final Function f) throws DatabaseException {
289 final SCLContext context = SCLContext.getCurrent();
290 VirtualGraphSupport vgs = graph.getService(VirtualGraphSupport.class);
291 VirtualGraph vg = vgs.getMemoryPersistent(virtualGraphId);
292 return graph.syncRequest(new WriteResultRequest<T>(vg) {
294 public T perform(WriteGraph graph) throws DatabaseException {
295 SCLContext.push(context);
296 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
298 return (T)f.apply(Tuple0.INSTANCE);
300 context.put(GRAPH, oldGraph);
307 public static <T> T virtualSyncWriteWS(WriteGraph graph, String virtualGraphId, final Function f) throws DatabaseException {
308 final SCLContext context = SCLContext.getCurrent();
309 VirtualGraphSupport vgs = graph.getService(VirtualGraphSupport.class);
310 VirtualGraph vg = vgs.getWorkspacePersistent(virtualGraphId);
311 return graph.syncRequest(new WriteResultRequest<T>(vg) {
313 public T perform(WriteGraph graph) throws DatabaseException {
314 SCLContext.push(context);
315 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
317 return (T)f.apply(Tuple0.INSTANCE);
319 context.put(GRAPH, oldGraph);
326 public static <T> T readValue(final String uri) throws DatabaseException {
327 return Simantics.getSession().syncRequest(new Read<T>() {
329 public T perform(ReadGraph graph) throws DatabaseException {
330 return Variables.getVariable(graph, uri).getValue(graph);
335 public static <T> void writeValue(final String uri, final T value) throws DatabaseException {
336 Simantics.getSession().syncRequest(new WriteRequest() {
338 public void perform(WriteGraph graph) throws DatabaseException {
339 Variables.getVariable(graph, uri).setValue(graph, value);
344 public static void activateOnce(Resource r) {
345 Simantics.getSession().getService(IActivationManager.class).activateOnce(r);
348 public static void syncActivateOnce(WriteGraph graph, Resource r) throws DatabaseException {
349 graph.getService(IActivationManager.class).activateOnce(graph, r);
352 public static Resource resourceFromId(ReadGraph graph, long id) throws DatabaseException, IOException {
353 SerialisationSupport ss = graph.getService(SerialisationSupport.class);
354 return ss.getResource(id);
357 public static void disableDependencies(WriteGraph graph) {
358 Layer0Utils.setDependenciesIndexingDisabled(graph, true);
361 public static void enableDependencies(WriteGraph graph) {
362 Layer0Utils.setDependenciesIndexingDisabled(graph, false);
365 public static void collectClusters() {
366 Simantics.getSession().getService(ClusterControl.class).collectClusters(Integer.MAX_VALUE);
369 public static class SCLUnaryRead extends BinaryRead<Function1<Object,Object>, Object, Object> {
371 public SCLUnaryRead(Function1<Object, Object> parameter1, Object parameter2) {
372 super(parameter1, parameter2);
376 public Object perform(ReadGraph graph) throws DatabaseException {
377 return Simantics.applySCLRead(graph, parameter, parameter2);
382 public static Object unaryQuery(ReadGraph graph, Function1<Object,Object> fn, Object value) throws DatabaseException {
383 return graph.syncRequest(new SCLUnaryRead(fn, value));
386 public static Object unaryQueryCached(ReadGraph graph, Function1<Object,Object> fn, Object value) throws DatabaseException {
387 return graph.syncRequest(new SCLUnaryRead(fn, value), TransientCacheAsyncListener.<Object>instance());
391 private static class Subquery extends UnaryRead<Function, Object> {
393 public Subquery(Function q) {
398 public Object perform(ReadGraph graph) throws DatabaseException {
399 return Simantics.applySCLRead(graph, parameter, Tuple0.INSTANCE);
404 public static Object subquery(ReadGraph graph, Function q) throws DatabaseException {
405 return graph.syncRequest(new Subquery(q));
408 public static Object subqueryC(ReadGraph graph, Function q) throws DatabaseException {
409 return graph.syncRequest(new Subquery(q), TransientCacheAsyncListener.<Object>instance());
412 public static void subqueryL(ReadGraph graph, Function query, Function executeCallback, Function1<Throwable, Tuple> exceptionCallback, Function1<Tuple0, Boolean> isDisposedCallback) throws DatabaseException {
413 graph.syncRequest(new Subquery(query), new SyncListenerAdapter<Object>() {
415 public void execute(ReadGraph graph, Object result) throws DatabaseException {
416 Simantics.applySCLRead(graph, executeCallback, result);
420 public void exception(ReadGraph graph, Throwable t) throws DatabaseException {
421 Simantics.applySCLRead(graph, exceptionCallback, t);
425 public boolean isDisposed() {
426 return isDisposedCallback.apply(Tuple0.INSTANCE);
431 public static Object possibleFromDynamic(Type expectedType, String moduleName, Object value) {
436 Failable<Module> failable = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName);
437 Module module = failable.getResult();
439 RuntimeEnvironment env = SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
440 EnvironmentSpecification.of(moduleName, ""), module.getParentClassLoader());
442 JavaTypeTranslator tr = new JavaTypeTranslator(env.getEnvironment());
443 TypeDesc desc = tr.toTypeDesc(expectedType);
444 String className = desc.getFullName();
445 Class<?> clazz = env.getMutableClassLoader().loadClass(className);
446 if (!clazz.isAssignableFrom(value.getClass()))
449 } catch (ImportFailureException | ClassNotFoundException e) {
454 public static void restrictQueries(ReadGraph graph, int amount, int step, int maxTimeInMs) {
456 QueryControl qc = graph.getService(QueryControl.class);
457 long start = System.currentTimeMillis();
459 int current = qc.count();
460 if(current < amount) return;
462 long duration = System.currentTimeMillis() - start;
463 if(duration > maxTimeInMs) return;
468 public static int countQueries(ReadGraph graph) {
470 QueryControl qc = graph.getService(QueryControl.class);