X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scl.db%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fdb%2FSCLFunctions.java;h=e20dfc471ad9ea28831463f00bc9bfc220d8704a;hp=9b4d6b8ac92ec90d5c3970285efbe15d7550c008;hb=4c77968eba9c464a32c8f1b5bf4b65802bdea1ff;hpb=c26409b1caf2f1e560d37c5befd11b442399c3fe diff --git a/bundles/org.simantics.scl.db/src/org/simantics/scl/db/SCLFunctions.java b/bundles/org.simantics.scl.db/src/org/simantics/scl/db/SCLFunctions.java index 9b4d6b8ac..e20dfc471 100644 --- a/bundles/org.simantics.scl.db/src/org/simantics/scl/db/SCLFunctions.java +++ b/bundles/org.simantics.scl.db/src/org/simantics/scl/db/SCLFunctions.java @@ -1,16 +1,21 @@ package org.simantics.scl.db; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.cojen.classfile.TypeDesc; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.VirtualGraph; import org.simantics.db.WriteGraph; +import org.simantics.db.common.procedure.adapter.SyncListenerAdapter; import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; import org.simantics.db.common.request.BinaryRead; import org.simantics.db.common.request.DelayedWriteRequest; import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.UnaryRead; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.request.WriteResultRequest; import org.simantics.db.exception.DatabaseException; @@ -18,56 +23,166 @@ import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.db.layer0.variable.Variables; import org.simantics.db.request.Read; import org.simantics.db.service.ClusterControl; +import org.simantics.db.service.QueryControl; import org.simantics.db.service.SerialisationSupport; import org.simantics.db.service.VirtualGraphSupport; import org.simantics.layer0.utils.triggers.IActivationManager; +import org.simantics.scl.compiler.elaboration.modules.SCLValue; +import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification; +import org.simantics.scl.compiler.errors.DoesNotExist; +import org.simantics.scl.compiler.errors.Failable; +import org.simantics.scl.compiler.errors.Failure; +import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator; +import org.simantics.scl.compiler.module.Module; +import org.simantics.scl.compiler.module.repository.ImportFailureException; +import org.simantics.scl.compiler.runtime.RuntimeEnvironment; +import org.simantics.scl.compiler.runtime.RuntimeModule; +import org.simantics.scl.compiler.top.ValueNotFound; +import org.simantics.scl.compiler.types.TCon; +import org.simantics.scl.compiler.types.Type; +import org.simantics.scl.compiler.types.Types; +import org.simantics.scl.compiler.types.exceptions.MatchException; +import org.simantics.scl.compiler.types.util.MultiFunction; +import org.simantics.scl.osgi.SCLOsgi; +import org.simantics.scl.reflection.ValueNotFoundException; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function; import org.simantics.scl.runtime.function.Function1; +import org.simantics.scl.runtime.reporting.SCLReportingHandler; +import org.simantics.scl.runtime.tuple.Tuple; import org.simantics.scl.runtime.tuple.Tuple0; import org.simantics.utils.DataContainer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @SuppressWarnings({"rawtypes", "unchecked"}) public class SCLFunctions { + private static final Logger LOGGER = LoggerFactory.getLogger(SCLFunctions.class); + public static final String GRAPH = "graph"; public static T safeExec(final Function f) { try { return (T)f.apply(Tuple0.INSTANCE); } catch (Throwable t) { - t.printStackTrace(); + LOGGER.error("safeExec caught exception", t); return null; } } + + public static Function resolveFunction(RuntimeModule rm, String function) throws ValueNotFound { + return (Function)rm.getValue(function); + } + + private static SCLValue resolveSCLValue(RuntimeModule rm, String function) throws ValueNotFound { + return rm.getModule().getValue(function); + } + + private static RuntimeModule resolveRuntimeModule(String module) throws ValueNotFound { + Failable f = SCLOsgi.MODULE_REPOSITORY.getRuntimeModule(module); + if(f.didSucceed()) + return f.getResult(); + else if(f == DoesNotExist.INSTANCE) + throw new ValueNotFound("Didn't find module " + module); + else + throw new ValueNotFound(((Failure)f).toString()); + } + + private static List getEffects(SCLValue value) throws ValueNotFoundException, ValueNotFound, MatchException { + + Type type = value.getType(); + MultiFunction mfun = Types.matchFunction(type, 1); + ArrayList concreteEffects = new ArrayList<>(); + mfun.effect.collectConcreteEffects(concreteEffects); + return concreteEffects; + + } + + public static List getEffects(RuntimeModule rm, String function) throws ValueNotFoundException, ValueNotFound, MatchException { + return getEffects(resolveSCLValue(rm, function)); + } + + public static List getEffects(String module, String function) throws ValueNotFoundException, ValueNotFound, MatchException { + return getEffects(resolveSCLValue(resolveRuntimeModule(module), function)); + } - public static void asyncRead(final Function f) throws DatabaseException { + private static T evaluate(Function function, Object ... args) { + return (T)function.applyArray(args); + } + + private static T evaluate(RuntimeModule rm, String function, Object ... args) throws ValueNotFound { + return evaluate(resolveFunction(rm, function)); + } + + public static T evaluate(String module, String function, Object ... args) throws ValueNotFound { + return evaluate(resolveRuntimeModule(module), function, args); + } + + public static T evaluateDB(String module, String function, Object ... args) throws DatabaseException { + try { + RuntimeModule rm = resolveRuntimeModule(module); + List effects = getEffects(resolveSCLValue(rm, function)); + Function f = resolveFunction(rm, function); + if(effects.contains(Types.WRITE_GRAPH)) { + return syncWrite(f, args); + } else if(effects.contains(Types.READ_GRAPH)) { + return syncRead(f, args); + } else { + return evaluate(f, args); + } + } catch (ValueNotFound e) { + throw new DatabaseException("SCL Value not found: " + e.name); + } catch (Throwable t) { + if (t instanceof DatabaseException) + throw (DatabaseException) t; + throw new DatabaseException(t); + } + } + + public static T evaluateGraph(String module, String function, Object graph, Object ... args) throws DatabaseException { final SCLContext context = SCLContext.getCurrent(); - Object graph = context.get(GRAPH); - if (graph != null) { - f.apply(Tuple0.INSTANCE); - } else { - Simantics.getSession().asyncRequest(new ReadRequest() { - @Override - public void run(ReadGraph graph) throws DatabaseException { - SCLContext.push(context); - ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph); - try { - f.apply(Tuple0.INSTANCE); - } finally { - context.put(GRAPH, oldGraph); - SCLContext.pop(); - } - } - }); + SCLContext.push(context); + Object oldGraph = context.put(GRAPH, graph); + try { + return evaluateDB(module, function, args); + } finally { + context.put(GRAPH, oldGraph); + SCLContext.pop(); } } + + private static Object[] NO_ARGS = new Object[] { Tuple0.INSTANCE }; + + public static void asyncRead(final Function f) throws DatabaseException { + asyncRead(f, NO_ARGS); + } + + public static void asyncRead(final Function f, final Object ... args) throws DatabaseException { + final SCLContext context = SCLContext.createDerivedContext(); + Simantics.getSession().asyncRequest(new ReadRequest() { + @Override + public void run(ReadGraph graph) throws DatabaseException { + SCLContext.push(context); + context.put(GRAPH, graph); + try { + f.apply(Tuple0.INSTANCE); + } finally { + SCLContext.pop(); + } + } + }); + } public static T syncRead(final Function f) throws DatabaseException { + return syncRead(f, NO_ARGS); + } + + public static T syncRead(final Function f, final Object ... args) throws DatabaseException { final SCLContext context = SCLContext.getCurrent(); Object graph = context.get(GRAPH); if (graph != null) { - return (T)f.apply(Tuple0.INSTANCE); + return (T)f.applyArray(args); } else { return Simantics.getSession().syncRequest(new Read() { @Override @@ -84,44 +199,53 @@ public class SCLFunctions { }); } } - + public static void asyncWrite(final Function f) throws DatabaseException { - final SCLContext context = SCLContext.getCurrent(); - Object graph = context.get(GRAPH); - if (graph != null) { - f.apply(Tuple0.INSTANCE); - } else { + asyncWrite(f, NO_ARGS); + } + + public static void asyncWrite(final Function f, final Object ... args) throws DatabaseException { + SCLContext context = SCLContext.createDerivedContext(); + if (Simantics.peekSession() != null) { Simantics.getSession().asyncRequest(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { SCLContext.push(context); - ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph); + context.put(GRAPH, graph); try { - f.apply(Tuple0.INSTANCE); + f.apply(args); } finally { - context.put(GRAPH, oldGraph); SCLContext.pop(); } } }); + } else { + LOGGER.warn("No session available for asynchronous write requests"); } } public static T syncWrite(final Function f) throws DatabaseException { + return syncWrite(f, NO_ARGS); + } + + public static T syncWrite(final Function f, final Object ... args) throws DatabaseException { final SCLContext context = SCLContext.getCurrent(); Object graph = context.get(GRAPH); if (graph != null) { return (T)f.apply(Tuple0.INSTANCE); } else { + final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER); return Simantics.getSession().syncRequest(new WriteResultRequest() { @Override public T perform(WriteGraph graph) throws DatabaseException { SCLContext.push(context); + SCLReportingHandler oldPrinter = (SCLReportingHandler)context.put(SCLReportingHandler.REPORTING_HANDLER, printer); ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph); try { - return (T)f.apply(Tuple0.INSTANCE); + return (T)f.apply(args); } finally { context.put(GRAPH, oldGraph); + context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter); SCLContext.pop(); } } @@ -131,13 +255,13 @@ public class SCLFunctions { public static T delayedSyncWrite(final Function f) throws DatabaseException { final SCLContext context = SCLContext.getCurrent(); - final DataContainer dc = new DataContainer(null); + final DataContainer dc = new DataContainer(null); DelayedWriteRequest request = new DelayedWriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { - final SCLContext context = SCLContext.getCurrent(); - SCLContext.push(context); + final SCLContext context = SCLContext.getCurrent(); + SCLContext.push(context); ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph); try { dc.set((T)f.apply(Tuple0.INSTANCE)); @@ -147,18 +271,18 @@ public class SCLFunctions { } } }; - + Object graph = context.get(GRAPH); if (graph != null) { if (graph instanceof WriteGraph) { - ((WriteGraph)graph).syncRequest(request); + ((WriteGraph)graph).syncRequest(request); } else { - throw new DatabaseException("Caller is inside a read transaction."); + throw new DatabaseException("Caller is inside a read transaction."); } } else { Simantics.getSession().syncRequest(request); } - return dc.get(); + return dc.get(); } public static T virtualSyncWriteMem(WriteGraph graph, String virtualGraphId, final Function f) throws DatabaseException { @@ -204,7 +328,7 @@ public class SCLFunctions { @Override public T perform(ReadGraph graph) throws DatabaseException { return Variables.getVariable(graph, uri).getValue(graph); - } + } }); } @@ -213,7 +337,7 @@ public class SCLFunctions { @Override public void perform(WriteGraph graph) throws DatabaseException { Variables.getVariable(graph, uri).setValue(graph, value); - } + } }); } @@ -244,63 +368,37 @@ public class SCLFunctions { public static class SCLUnaryRead extends BinaryRead, Object, Object> { - public SCLUnaryRead(Function1 parameter1, Object parameter2) { - super(parameter1, parameter2); - } + public SCLUnaryRead(Function1 parameter1, Object parameter2) { + super(parameter1, parameter2); + } + + @Override + public Object perform(ReadGraph graph) throws DatabaseException { + return Simantics.applySCLRead(graph, parameter, parameter2); + } - @Override - public Object perform(ReadGraph graph) throws DatabaseException { - return Simantics.applySCLRead(graph, parameter, parameter2); - } - } public static Object unaryQuery(ReadGraph graph, Function1 fn, Object value) throws DatabaseException { - return graph.syncRequest(new SCLUnaryRead(fn, value)); + return graph.syncRequest(new SCLUnaryRead(fn, value)); } public static Object unaryQueryCached(ReadGraph graph, Function1 fn, Object value) throws DatabaseException { - return graph.syncRequest(new SCLUnaryRead(fn, value), TransientCacheAsyncListener.instance()); + return graph.syncRequest(new SCLUnaryRead(fn, value), TransientCacheAsyncListener.instance()); } - private static class Subquery implements Read { - Function q; + private static class Subquery extends UnaryRead { public Subquery(Function q) { - this.q = q; + super(q); } @Override public Object perform(ReadGraph graph) throws DatabaseException { - SCLContext sclContext = SCLContext.getCurrent(); - Object oldGraph = sclContext.put("graph", graph); - try { - return q.apply(Tuple0.INSTANCE); - } catch (Throwable e) { - if(e instanceof DatabaseException) - throw (DatabaseException)e; - else - throw new DatabaseException(e); - } finally { - sclContext.put("graph", oldGraph); - } + return Simantics.applySCLRead(graph, parameter, Tuple0.INSTANCE); } - @Override - public int hashCode() { - return q.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(this == obj) - return true; - if(obj == null || obj.getClass() != getClass()) - return false; - Subquery other = (Subquery)obj; - return q.equals(other.q); - } } public static Object subquery(ReadGraph graph, Function q) throws DatabaseException { @@ -310,4 +408,68 @@ public class SCLFunctions { public static Object subqueryC(ReadGraph graph, Function q) throws DatabaseException { return graph.syncRequest(new Subquery(q), TransientCacheAsyncListener.instance()); } + + public static void subqueryL(ReadGraph graph, Function query, Function executeCallback, Function1 exceptionCallback, Function1 isDisposedCallback) throws DatabaseException { + graph.syncRequest(new Subquery(query), new SyncListenerAdapter() { + @Override + public void execute(ReadGraph graph, Object result) throws DatabaseException { + Simantics.applySCLRead(graph, executeCallback, result); + } + + @Override + public void exception(ReadGraph graph, Throwable t) throws DatabaseException { + Simantics.applySCLRead(graph, exceptionCallback, t); + } + + @Override + public boolean isDisposed() { + return isDisposedCallback.apply(Tuple0.INSTANCE); + } + }); + } + + public static Object possibleFromDynamic(Type expectedType, String moduleName, Object value) { + + try { + + + Failable failable = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName); + Module module = failable.getResult(); + + RuntimeEnvironment env = SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment( + EnvironmentSpecification.of(moduleName, ""), module.getParentClassLoader()); + + JavaTypeTranslator tr = new JavaTypeTranslator(env.getEnvironment()); + TypeDesc desc = tr.toTypeDesc(expectedType); + String className = desc.getFullName(); + Class clazz = env.getMutableClassLoader().loadClass(className); + if (!clazz.isAssignableFrom(value.getClass())) + return null; + + } catch (ImportFailureException | ClassNotFoundException e) { + } + return value; + } + + public static void restrictQueries(ReadGraph graph, int amount, int step, int maxTimeInMs) { + + QueryControl qc = graph.getService(QueryControl.class); + long start = System.currentTimeMillis(); + while(true) { + int current = qc.count(); + if(current < amount) return; + qc.gc(graph, step); + long duration = System.currentTimeMillis() - start; + if(duration > maxTimeInMs) return; + } + + } + + public static int countQueries(ReadGraph graph) { + + QueryControl qc = graph.getService(QueryControl.class); + return qc.count(); + + } + }