]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.db/src/org/simantics/scl/db/SCLFunctions.java
Improve utilities for invoking SCL from Java
[simantics/platform.git] / bundles / org.simantics.scl.db / src / org / simantics / scl / db / SCLFunctions.java
1 package org.simantics.scl.db;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.List;
6
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;
57
58 @SuppressWarnings({"rawtypes", "unchecked"})
59 public class SCLFunctions {
60
61     private static final Logger LOGGER = LoggerFactory.getLogger(SCLFunctions.class);
62
63     public static final String GRAPH = "graph";
64
65     public static <T> T safeExec(final Function f) {
66         try {
67             return (T)f.apply(Tuple0.INSTANCE);
68         } catch (Throwable t) {
69             LOGGER.error("safeExec caught exception", t);
70             return null;
71         }
72     }
73
74     public static Function resolveFunction(RuntimeModule rm, String function) throws ValueNotFound {
75         return (Function)rm.getValue(function);
76     }
77     
78     private static SCLValue resolveSCLValue(RuntimeModule rm, String function) throws ValueNotFound {
79         return rm.getModule().getValue(function);
80     }
81     
82     private static RuntimeModule resolveRuntimeModule(String module) throws ValueNotFound {
83         Failable<RuntimeModule> f = SCLOsgi.MODULE_REPOSITORY.getRuntimeModule(module);
84         if(f.didSucceed())
85             return f.getResult();
86         else if(f == DoesNotExist.INSTANCE)
87             throw new ValueNotFound("Didn't find module " + module);
88         else
89             throw new ValueNotFound(((Failure)f).toString());
90     }
91     
92     private static List<TCon> getEffects(SCLValue value) throws ValueNotFoundException, ValueNotFound, MatchException {
93     
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;
99         
100     }
101
102     public static List<TCon> getEffects(RuntimeModule rm, String function) throws ValueNotFoundException, ValueNotFound, MatchException {
103         return getEffects(resolveSCLValue(rm, function));
104     }
105
106     public static List<TCon> getEffects(String module, String function) throws ValueNotFoundException, ValueNotFound, MatchException {
107         return getEffects(resolveSCLValue(resolveRuntimeModule(module), function));
108     }
109     
110     private static <T> T evaluate(Function function, Object ... args) {
111         return (T)function.applyArray(args);
112     }
113
114     private static <T> T evaluate(RuntimeModule rm, String function, Object ... args) throws ValueNotFound {
115         return evaluate(resolveFunction(rm, function));
116     }
117
118     public static <T> T evaluate(String module, String function, Object ... args) throws ValueNotFound {
119         return evaluate(resolveRuntimeModule(module), function, args);
120     }
121
122     public static <T> T evaluateDB(String module, String function, Object ... args) throws DatabaseException {
123         try {
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);
131             } else {
132                 return evaluate(f, args);
133             }
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);
140         }
141     }
142     
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);
147         try {
148             return evaluateDB(module, function, args);
149         } finally {
150             context.put(GRAPH, oldGraph);
151             SCLContext.pop();
152         }
153     }
154
155     private static Object[] NO_ARGS = new Object[] { Tuple0.INSTANCE };
156
157     public static <T> void asyncRead(final Function f) throws DatabaseException {    
158         asyncRead(f, NO_ARGS);
159     }
160
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() {
164             @Override
165             public void run(ReadGraph graph) throws DatabaseException {
166                 SCLContext.push(context);
167                 context.put(GRAPH, graph);
168                 try {
169                     f.apply(Tuple0.INSTANCE);
170                 } finally {
171                     SCLContext.pop();
172                 }
173             }
174         });
175     }
176     
177     public static <T> T syncRead(final Function f) throws DatabaseException {
178         return syncRead(f, NO_ARGS);
179     }
180     
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);
184         if (graph != null) {
185             return (T)f.applyArray(args);
186         } else {
187             return Simantics.getSession().syncRequest(new Read<T>() {
188                 @Override
189                 public T perform(ReadGraph graph) throws DatabaseException {
190                     SCLContext.push(context);
191                     ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
192                     try {
193                         return (T)f.apply(Tuple0.INSTANCE);
194                     } finally {
195                         context.put(GRAPH, oldGraph);
196                         SCLContext.pop();
197                     }
198                 }
199             });
200         }
201     }
202
203     public static void asyncWrite(final Function f) throws DatabaseException {
204         asyncWrite(f, NO_ARGS);
205     }
206
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() {
211                 @Override
212                 public void perform(WriteGraph graph) throws DatabaseException {
213                     SCLContext.push(context);
214                     context.put(GRAPH, graph);
215                     try {
216                         f.apply(args);
217                     } finally {
218                         SCLContext.pop();
219                     }
220                 }
221             });
222         } else {
223             LOGGER.warn("No session available for asynchronous write requests");
224         }
225     }
226     
227     public static <T> T syncWrite(final Function f) throws DatabaseException {
228         return syncWrite(f, NO_ARGS);
229     }
230
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);
234         if (graph != null) {
235             return (T)f.apply(Tuple0.INSTANCE);
236         } else {
237             final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
238             return Simantics.getSession().syncRequest(new WriteResultRequest<T>() {
239                 @Override
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);
244                     try {
245                         return (T)f.apply(args);
246                     } finally {
247                         context.put(GRAPH, oldGraph);
248                         context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
249                         SCLContext.pop();
250                     }
251                 }
252             });
253         }
254     }
255     
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);
259
260         DelayedWriteRequest request = new DelayedWriteRequest() {
261             @Override
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);
266                 try {
267                     dc.set((T)f.apply(Tuple0.INSTANCE));
268                 } finally {
269                     context.put(GRAPH, oldGraph);
270                     SCLContext.pop();
271                 }
272             }
273         };
274     
275         Object graph = context.get(GRAPH);
276         if (graph != null) {
277             if (graph instanceof WriteGraph) {
278                 ((WriteGraph)graph).syncRequest(request);
279             } else {
280                 throw new DatabaseException("Caller is inside a read transaction.");
281             }
282         } else {
283             Simantics.getSession().syncRequest(request);
284         }
285         return dc.get();
286     }
287
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) {
293             @Override
294             public T perform(WriteGraph graph) throws DatabaseException {
295                 SCLContext.push(context);
296                 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
297                 try {
298                     return (T)f.apply(Tuple0.INSTANCE);
299                 } finally {
300                     context.put(GRAPH, oldGraph);
301                     SCLContext.pop();
302                 }
303             }
304         });
305     }
306     
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) {
312             @Override
313             public T perform(WriteGraph graph) throws DatabaseException {
314                 SCLContext.push(context);
315                 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
316                 try {
317                     return (T)f.apply(Tuple0.INSTANCE);
318                 } finally {
319                     context.put(GRAPH, oldGraph);
320                     SCLContext.pop();
321                 }
322             }
323         });
324     }
325     
326     public static <T> T readValue(final String uri) throws DatabaseException {
327         return Simantics.getSession().syncRequest(new Read<T>() {
328             @Override
329             public T perform(ReadGraph graph) throws DatabaseException {
330                 return Variables.getVariable(graph, uri).getValue(graph);
331             }
332         });
333     }
334     
335     public static <T> void writeValue(final String uri, final T value) throws DatabaseException {
336         Simantics.getSession().syncRequest(new WriteRequest() {
337             @Override
338             public void perform(WriteGraph graph) throws DatabaseException {
339                 Variables.getVariable(graph, uri).setValue(graph, value);
340             }
341         });
342     }
343     
344     public static void activateOnce(Resource r) {
345         Simantics.getSession().getService(IActivationManager.class).activateOnce(r);
346     }
347     
348     public static void syncActivateOnce(WriteGraph graph, Resource r) throws DatabaseException {
349         graph.getService(IActivationManager.class).activateOnce(graph, r);
350     }
351
352     public static Resource resourceFromId(ReadGraph graph, long id) throws DatabaseException, IOException {
353         SerialisationSupport ss = graph.getService(SerialisationSupport.class);
354         return ss.getResource(id);
355     }
356     
357     public static void disableDependencies(WriteGraph graph) {
358         Layer0Utils.setDependenciesIndexingDisabled(graph, true);       
359     }
360     
361     public static void enableDependencies(WriteGraph graph) {
362         Layer0Utils.setDependenciesIndexingDisabled(graph, false);       
363     }
364     
365     public static void collectClusters() {
366         Simantics.getSession().getService(ClusterControl.class).collectClusters(Integer.MAX_VALUE);
367     }
368     
369     public static class SCLUnaryRead extends BinaryRead<Function1<Object,Object>, Object, Object> {
370
371         public SCLUnaryRead(Function1<Object, Object> parameter1, Object parameter2) {
372              super(parameter1, parameter2);
373         }
374
375         @Override
376         public Object perform(ReadGraph graph) throws DatabaseException {
377             return Simantics.applySCLRead(graph, parameter, parameter2);
378         }
379
380     }
381     
382     public static Object unaryQuery(ReadGraph graph, Function1<Object,Object> fn, Object value) throws DatabaseException {
383         return graph.syncRequest(new SCLUnaryRead(fn, value));
384     }
385
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());
388     }
389     
390
391     private static class Subquery extends UnaryRead<Function, Object> {
392
393         public Subquery(Function q) {
394             super(q);
395         }
396
397         @Override
398         public Object perform(ReadGraph graph) throws DatabaseException {
399             return Simantics.applySCLRead(graph, parameter, Tuple0.INSTANCE);
400         }
401
402     }
403
404     public static Object subquery(ReadGraph graph, Function q) throws DatabaseException {
405         return graph.syncRequest(new Subquery(q));
406     }
407
408     public static Object subqueryC(ReadGraph graph, Function q) throws DatabaseException {
409         return graph.syncRequest(new Subquery(q), TransientCacheAsyncListener.<Object>instance());
410     }
411     
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>() {
414             @Override
415             public void execute(ReadGraph graph, Object result) throws DatabaseException {
416                 Simantics.applySCLRead(graph, executeCallback, result);
417             }
418             
419             @Override
420             public void exception(ReadGraph graph, Throwable t) throws DatabaseException {
421                 Simantics.applySCLRead(graph, exceptionCallback, t);
422             }
423             
424             @Override
425             public boolean isDisposed() {
426                 return isDisposedCallback.apply(Tuple0.INSTANCE);
427             }
428         });
429     }
430
431     public static Object possibleFromDynamic(Type expectedType, String moduleName, Object value) {
432     
433         try {
434
435         
436             Failable<Module> failable = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName);
437             Module module = failable.getResult();
438             
439             RuntimeEnvironment env = SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
440             EnvironmentSpecification.of(moduleName, ""), module.getParentClassLoader());
441
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()))
447                 return null;
448             
449         } catch (ImportFailureException | ClassNotFoundException e) {
450         }
451         return value;
452     }
453
454     public static void restrictQueries(ReadGraph graph, int amount, int step, int maxTimeInMs) {
455
456         QueryControl qc = graph.getService(QueryControl.class);
457         long start = System.currentTimeMillis();
458         while(true) {
459             int current = qc.count();
460             if(current < amount) return;
461             qc.gc(graph, step);
462             long duration = System.currentTimeMillis() - start;
463             if(duration > maxTimeInMs) return;
464         }
465
466     }
467
468     public static int countQueries(ReadGraph graph) {
469
470         QueryControl qc = graph.getService(QueryControl.class);
471         return qc.count();
472
473     }
474     
475 }