Simantics JDBC testing with access over Variable/StandardNodeManager 91/1891/3 feature/jdbc-simantics
authorjsimomaa <jani.simomaa@gmail.com>
Sat, 30 Jun 2018 12:31:02 +0000 (15:31 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 18 Sep 2018 07:02:10 +0000 (07:02 +0000)
gitlab #125

Change-Id: I8dc2cab179e941dca25a2683c1d4f444cb2ae904

29 files changed:
bundles/org.simantics.jdbc.ontology/.classpath [new file with mode: 0644]
bundles/org.simantics.jdbc.ontology/.project [new file with mode: 0644]
bundles/org.simantics.jdbc.ontology/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
bundles/org.simantics.jdbc.ontology/META-INF/MANIFEST.MF [new file with mode: 0644]
bundles/org.simantics.jdbc.ontology/build.properties [new file with mode: 0644]
bundles/org.simantics.jdbc.ontology/graph/JDBC.pgraph [new file with mode: 0644]
bundles/org.simantics.jdbc.ontology/src/org/simantics/jdbc/ontology/Activator.java [new file with mode: 0644]
bundles/org.simantics.jdbc/.classpath [new file with mode: 0644]
bundles/org.simantics.jdbc/.project [new file with mode: 0644]
bundles/org.simantics.jdbc/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
bundles/org.simantics.jdbc/META-INF/MANIFEST.MF [new file with mode: 0644]
bundles/org.simantics.jdbc/adapters.xml [new file with mode: 0644]
bundles/org.simantics.jdbc/build.properties [new file with mode: 0644]
bundles/org.simantics.jdbc/jdbc.properties [new file with mode: 0644]
bundles/org.simantics.jdbc/src/org/simantics/jdbc/SimanticsJDBC.java [new file with mode: 0644]
bundles/org.simantics.jdbc/src/org/simantics/jdbc/internal/Activator.java [new file with mode: 0644]
bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNode.java [new file with mode: 0644]
bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManager.java [new file with mode: 0644]
bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManagerSupport.java [new file with mode: 0644]
bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCRealm.java [new file with mode: 0644]
bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCSessionManager.java [new file with mode: 0644]
bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCVariableBuilder.java [new file with mode: 0644]
features/org.simantics.jdbc.feature/.project [new file with mode: 0644]
features/org.simantics.jdbc.feature/build.properties [new file with mode: 0644]
features/org.simantics.jdbc.feature/feature.xml [new file with mode: 0644]
releng/org.simantics.sdk.build.p2.site/pom.xml
releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target
releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd
releng/org.simantics.sdk.build.targetdefinition/simantics.target

diff --git a/bundles/org.simantics.jdbc.ontology/.classpath b/bundles/org.simantics.jdbc.ontology/.classpath
new file mode 100644 (file)
index 0000000..eca7bdb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.simantics.jdbc.ontology/.project b/bundles/org.simantics.jdbc.ontology/.project
new file mode 100644 (file)
index 0000000..6668cd1
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.simantics.jdbc.ontology</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.simantics.graph.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.simantics.graph.nature</nature>
+       </natures>
+</projectDescription>
diff --git a/bundles/org.simantics.jdbc.ontology/.settings/org.eclipse.jdt.core.prefs b/bundles/org.simantics.jdbc.ontology/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..0c68a61
--- /dev/null
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/bundles/org.simantics.jdbc.ontology/META-INF/MANIFEST.MF b/bundles/org.simantics.jdbc.ontology/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..69dae10
--- /dev/null
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: http://www.simantics.org/JDBC-1.0
+Bundle-SymbolicName: org.simantics.jdbc.ontology
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.simantics.jdbc.ontology.Activator
+Require-Bundle: org.eclipse.core.runtime,
+ org.simantics.layer0,
+ org.simantics.selectionview.ontology;bundle-version="1.2.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Export-Package: org.simantics.jdbc
diff --git a/bundles/org.simantics.jdbc.ontology/build.properties b/bundles/org.simantics.jdbc.ontology/build.properties
new file mode 100644 (file)
index 0000000..e85b630
--- /dev/null
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               graph.tg
diff --git a/bundles/org.simantics.jdbc.ontology/graph/JDBC.pgraph b/bundles/org.simantics.jdbc.ontology/graph/JDBC.pgraph
new file mode 100644 (file)
index 0000000..432e10c
--- /dev/null
@@ -0,0 +1,10 @@
+L0 = <http://www.simantics.org/Layer0-1.1>
+SEL = <http://www.simantics.org/SelectionView-1.2>
+
+JDBC = <http://www.simantics.org/JDBC-1.0> : L0.Ontology
+    @L0.new
+    L0.HasResourceClass "org.simantics.jdbc.JDBCResource"
+    
+JDBC.Session <T L0.Entity
+    >-- JDBC.Session.hasValue --> L0.Value <R L0.HasProperty : SEL.GenericParameterType
\ No newline at end of file
diff --git a/bundles/org.simantics.jdbc.ontology/src/org/simantics/jdbc/ontology/Activator.java b/bundles/org.simantics.jdbc.ontology/src/org/simantics/jdbc/ontology/Activator.java
new file mode 100644 (file)
index 0000000..596e25d
--- /dev/null
@@ -0,0 +1,30 @@
+package org.simantics.jdbc.ontology;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class Activator implements BundleActivator {
+
+       private static BundleContext context;
+
+       static BundleContext getContext() {
+               return context;
+       }
+
+       /*
+        * (non-Javadoc)
+        * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+        */
+       public void start(BundleContext bundleContext) throws Exception {
+               Activator.context = bundleContext;
+       }
+
+       /*
+        * (non-Javadoc)
+        * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+        */
+       public void stop(BundleContext bundleContext) throws Exception {
+               Activator.context = null;
+       }
+
+}
diff --git a/bundles/org.simantics.jdbc/.classpath b/bundles/org.simantics.jdbc/.classpath
new file mode 100644 (file)
index 0000000..b862a29
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+       <classpathentry kind="src" path="src"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/bundles/org.simantics.jdbc/.project b/bundles/org.simantics.jdbc/.project
new file mode 100644 (file)
index 0000000..3af9a2f
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.simantics.jdbc</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.simantics.graph.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.simantics.graph.nature</nature>
+       </natures>
+</projectDescription>
diff --git a/bundles/org.simantics.jdbc/.settings/org.eclipse.jdt.core.prefs b/bundles/org.simantics.jdbc/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..0c68a61
--- /dev/null
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/bundles/org.simantics.jdbc/META-INF/MANIFEST.MF b/bundles/org.simantics.jdbc/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..9aff2f4
--- /dev/null
@@ -0,0 +1,24 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Simantics JDBC support
+Bundle-SymbolicName: org.simantics.jdbc
+Bundle-Version: 1.0.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ClassPath: .
+Require-Bundle: org.eclipse.osgi,
+ org.simantics.simulator.toolkit,
+ org.simantics.databoard,
+ gnu.trove3;bundle-version="3.0.3",
+ org.slf4j.api;bundle-version="1.7.25",
+ org.simantics.db.layer0,
+ org.simantics.simulator.toolkit.db,
+ org.simantics,
+ org.simantics.jdbc.ontology,
+ io.netty.buffer;bundle-version="4.1.27",
+ io.netty.codec;bundle-version="4.1.27",
+ io.netty.common;bundle-version="4.1.27",
+ io.netty.handler;bundle-version="4.1.27",
+ io.netty.transport;bundle-version="4.1.27",
+ pgjdbc-ng;bundle-version="0.7.1"
+Export-Package: org.simantics.jdbc,
+ org.simantics.jdbc.variable
diff --git a/bundles/org.simantics.jdbc/adapters.xml b/bundles/org.simantics.jdbc/adapters.xml
new file mode 100644 (file)
index 0000000..800ee14
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<adapters>
+    <target interface="org.simantics.db.layer0.variable.VariableBuilder">
+        <type uri="http://www.simantics.org/JDBC-1.0/Session" class="org.simantics.jdbc.variable.JDBCVariableBuilder" />
+    </target>
+</adapters>
\ No newline at end of file
diff --git a/bundles/org.simantics.jdbc/build.properties b/bundles/org.simantics.jdbc/build.properties
new file mode 100644 (file)
index 0000000..0dc8dce
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               adapters.xml,\
+               jdbc.properties
diff --git a/bundles/org.simantics.jdbc/jdbc.properties b/bundles/org.simantics.jdbc/jdbc.properties
new file mode 100644 (file)
index 0000000..7f23859
--- /dev/null
@@ -0,0 +1,5 @@
+simantics.jdbc.host=127.0.0.1
+simantics.jdbc.port=5432
+simantics.jdbc.user=simantics
+simantics.jdbc.password=simantics
+simantics.jdbc.database=simantics
\ No newline at end of file
diff --git a/bundles/org.simantics.jdbc/src/org/simantics/jdbc/SimanticsJDBC.java b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/SimanticsJDBC.java
new file mode 100644 (file)
index 0000000..ac287e6
--- /dev/null
@@ -0,0 +1,45 @@
+package org.simantics.jdbc;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.layer0.Layer0;
+
+/**
+ * Simantics JDBC facade.
+ * 
+ * @author Jani Simomaa
+ */
+public class SimanticsJDBC {
+
+    public static final String PROP_SIMANTICS_JDBC_PROPERTYFILE = "simantics.jdbc.propertyfile";
+    public static final String PROP_SIMANTICS_JDBC_HOST         = "simantics.jdbc.host";
+    public static final String PROP_SIMANTICS_JDBC_PORT         = "simantics.jdbc.port";
+    public static final String PROP_SIMANTICS_JDBC_USER         = "simantics.jdbc.user";
+    public static final String PROP_SIMANTICS_JDBC_PASSWORD     = "simantics.jdbc.password";
+    public static final String PROP_SIMANTICS_JDBC_DATABASE     = "simantics.jdbc.database";
+
+    public static String createJDBCSession(WriteGraph graph, String sessionGUID) throws DatabaseException {
+        Resource projects = graph.getResource("http://Projects");
+        Resource documentSessions = Layer0Utils.getPossibleChild(graph, projects, "DocumentSessions");
+        Layer0 L0 = Layer0.getInstance(graph);
+        Resource documentSession = Layer0Utils.getPossibleChild(graph, documentSessions, sessionGUID);
+
+        String sessionId = "http://Projects/DocumentSessions/" + sessionGUID + "/__jdbc__";
+
+        JDBCResource JDBC = JDBCResource.getInstance(graph);
+        Resource jdbcSession = graph.newResource();
+        graph.claim(jdbcSession, L0.InstanceOf, JDBC.Session);
+        graph.claimLiteral(jdbcSession, L0.HasName, L0.NameOf, L0.String, "__jdbc__", Bindings.STRING);
+        graph.claim(documentSession, L0.ConsistsOf, jdbcSession);
+
+        @SuppressWarnings("unused")
+        Variable jdbcState = Variables.getVariable(graph, jdbcSession);
+
+        return sessionId;
+    }
+}
diff --git a/bundles/org.simantics.jdbc/src/org/simantics/jdbc/internal/Activator.java b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/internal/Activator.java
new file mode 100644 (file)
index 0000000..0f9da36
--- /dev/null
@@ -0,0 +1,18 @@
+package org.simantics.jdbc.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class Activator implements BundleActivator {
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        
+    }
+
+}
diff --git a/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNode.java b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNode.java
new file mode 100644 (file)
index 0000000..54f5d39
--- /dev/null
@@ -0,0 +1,41 @@
+package org.simantics.jdbc.variable;
+
+import org.simantics.simulator.toolkit.StandardNode;
+
+public class JDBCNode implements StandardNode {
+
+    public String name;
+
+    public JDBCNode(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        JDBCNode other = (JDBCNode) obj;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        return true;
+    }
+}
diff --git a/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManager.java b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManager.java
new file mode 100644 (file)
index 0000000..fd25852
--- /dev/null
@@ -0,0 +1,21 @@
+package org.simantics.jdbc.variable;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.simantics.simulator.toolkit.StandardNodeManager;
+import org.simantics.simulator.toolkit.StandardRealm;
+import org.simantics.simulator.variable.exceptions.NodeManagerException;
+
+public class JDBCNodeManager extends StandardNodeManager<JDBCNode, JDBCNodeManagerSupport> {
+
+    public JDBCNodeManager(StandardRealm<JDBCNode, JDBCNodeManagerSupport> realm, JDBCNode root) {
+        super(realm, root);
+    }
+
+    @Override
+    public Set<String> getClassifications(JDBCNode node) throws NodeManagerException {
+        return Collections.emptySet();
+    }
+
+}
diff --git a/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManagerSupport.java b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManagerSupport.java
new file mode 100644 (file)
index 0000000..b1c16a6
--- /dev/null
@@ -0,0 +1,123 @@
+package org.simantics.jdbc.variable;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.simulator.toolkit.StandardNodeManagerSupport;
+import org.simantics.simulator.variable.exceptions.NodeManagerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.impossibl.postgres.api.jdbc.PGConnection;
+import com.impossibl.postgres.jdbc.PGDataSource;
+
+public class JDBCNodeManagerSupport implements StandardNodeManagerSupport<JDBCNode> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(JDBCNodeManagerSupport.class);
+    @SuppressWarnings("unused")
+    private String id; // this might have some use later in the future?
+    private PGDataSource dataSource;
+    private String channelName;
+
+    public JDBCNodeManagerSupport(String id, PGDataSource dataSource, String channelName) {
+        this.id = id;
+        this.dataSource = dataSource;
+        this.channelName = channelName;
+    }
+
+    @Override
+    public Object getEngineValue(JDBCNode node) throws NodeManagerException {
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Getting value for {}", node.getName());
+
+        try (PGConnection connection = (PGConnection) dataSource.getConnection()) {
+            // do get value
+            PreparedStatement ps = connection.prepareStatement("SELECT value->'value' FROM simantics_table WHERE key IN ('" + node.getName() + "');");
+            ResultSet rs = ps.executeQuery();
+            if (!rs.next()) {
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debug("no value for query {}", ps.toString());
+                }
+                return null;
+            }
+            return rs.getObject(1);
+        } catch (Exception e) {
+            LOGGER.error("Failed to get value for {}", node.getName(), e);
+            throw new NodeManagerException("Failed to get value for " + node.getName(), e);
+        }
+    }
+
+    @Override
+    public Binding getEngineBinding(JDBCNode node) throws NodeManagerException {
+        return Bindings.OBJECT;
+    }
+
+    @Override
+    public void setEngineValue(JDBCNode node, Object value) throws NodeManagerException {
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Setting value for {} to {}", node.getName(), value);
+
+        setValueImpl(node.getName(), value);
+    }
+
+    private void setValueImpl(String name, Object value) throws NodeManagerException {
+        try (PGConnection connection = (PGConnection) dataSource.getConnection()) {
+            // do set value
+            PreparedStatement statement = connection.prepareStatement("INSERT INTO simantics_table VALUES (?, ?::JSON) ON CONFLICT (key) DO UPDATE SET value= ?::JSON");
+            statement.setString(1, name);
+            statement.setObject(2, "{\"value\": " + value.toString() + "}");
+            statement.setObject(3, "{\"value\": " + value.toString() + "}");
+            statement.executeUpdate();
+
+            // notify others (including ourselves)
+            doNotify(connection, name);
+        } catch (Exception e) {
+            LOGGER.error("Failed to set value for {} to {}", name, value, e);
+            throw new NodeManagerException("Failed to set value for " + name + " to " + String.valueOf(value), e);
+        }
+    }
+
+    private void doNotify(PGConnection connection, String name) throws SQLException {
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Notifying change {} to channel {}", name, this.channelName);
+        Statement statement = connection.createStatement();
+        String sql = "NOTIFY " + this.channelName + ", '" + name + "'";
+        statement.execute(sql);
+        statement.close();
+    }
+
+    @Override
+    public String getName(JDBCNode node) {
+        return node.getName();
+    }
+
+    @Override
+    public Map<String, JDBCNode> getChildren(JDBCNode node) {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<String, JDBCNode> getProperties(JDBCNode node) {
+        HashMap<String, JDBCNode> properties = new HashMap<>();
+        try (PGConnection connection = (PGConnection) dataSource.getConnection()) {
+            Statement st = connection.createStatement();
+            ResultSet executeQuery = st.executeQuery("SELECT key FROM simantics_table");
+            while (executeQuery.next()) {
+                String key = executeQuery.getString(1);
+                properties.put(key, new JDBCNode(key));
+            }
+        } catch (Exception e) {
+            LOGGER.error("Could not read properties", e);
+        }
+
+        return properties;
+    }
+
+}
diff --git a/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCRealm.java b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCRealm.java
new file mode 100644 (file)
index 0000000..9a6f4ab
--- /dev/null
@@ -0,0 +1,28 @@
+package org.simantics.jdbc.variable;
+
+import org.simantics.simulator.toolkit.StandardNodeManager;
+import org.simantics.simulator.toolkit.StandardRealm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JDBCRealm extends StandardRealm<JDBCNode, JDBCNodeManagerSupport> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(JDBCRealm.class);
+
+    protected JDBCRealm(JDBCNodeManagerSupport engine, String id) {
+        super(engine, id);
+    }
+
+    @Override
+    protected StandardNodeManager<JDBCNode, JDBCNodeManagerSupport> createManager() {
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Creating {} for realm with id {}", JDBCNodeManager.class.getSimpleName(), getId());
+        return new JDBCNodeManager(this, new JDBCNode("ROOT"));
+    }
+
+    @Override
+    public Logger getLogger() {
+        return LOGGER;
+    }
+
+}
diff --git a/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCSessionManager.java b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCSessionManager.java
new file mode 100644 (file)
index 0000000..f6a6077
--- /dev/null
@@ -0,0 +1,208 @@
+package org.simantics.jdbc.variable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+import java.util.function.Function;
+
+import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.NodeSupport;
+import org.simantics.jdbc.SimanticsJDBC;
+import org.simantics.simulator.toolkit.StandardRealm;
+import org.simantics.simulator.toolkit.db.StandardSessionManager;
+import org.simantics.simulator.variable.exceptions.NodeManagerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.impossibl.postgres.api.jdbc.PGConnection;
+import com.impossibl.postgres.api.jdbc.PGNotificationListener;
+import com.impossibl.postgres.jdbc.PGDataSource;
+
+public class JDBCSessionManager extends StandardSessionManager<JDBCNode, JDBCNodeManagerSupport> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(JDBCSessionManager.class);
+    private static JDBCSessionManager INSTANCE = new JDBCSessionManager();
+
+    private static final String VALUE_SIMANTICS_JDBC_HOST     = "localhost";
+    private static final int    VALUE_SIMANTICS_JDBC_PORT     = 5432;
+    private static final String VALUE_SIMANTICS_JDBC_USER     = "simantics";
+    private static final String VALUE_SIMANTICS_JDBC_PASSWORD = "simantics";
+    private static final String VALUE_SIMANTICS_JDBC_DATABASE = "simantics";
+
+    private String channelName;
+    private PGNotificationListener listener;
+
+    private PGDataSource dataSource;
+    private Connection connection;
+
+    private static Properties readProperties(InputStream s) throws IOException {
+        try (InputStream is = s) {
+            Properties props = new Properties();
+            props.load(is);
+            return props;
+        }
+    }
+
+    private static Properties safeReadProperties(URL url) {
+        try {
+            return readProperties(url.openStream());
+        } catch (IOException e) {
+            LOGGER.error("Could not read props from " + url, e);
+            return null;
+        }
+    }
+
+    private static Properties safeReadProperties(URI uri) {
+        try {
+            return safeReadProperties(uri.toURL());
+        } catch (MalformedURLException e) {
+            LOGGER.error("Could not read props from " + uri, e);
+            return null;
+        }
+    }
+
+    private static Properties safeReadProperties(String path) {
+        return safeReadProperties(Paths.get(path).toUri());
+    }
+
+    private static Properties readProperties() {
+        String propFile = System.getProperty(SimanticsJDBC.PROP_SIMANTICS_JDBC_PROPERTYFILE, null);
+        if (propFile != null) {
+            Properties p = safeReadProperties(propFile);
+            if (p != null)
+                return p;
+        }
+        // Read default settings from built-in file and override them with values in System properties 
+        Properties p = safeReadProperties(JDBCSessionManager.class.getClassLoader().getResource("jdbc.properties"));
+        if (p != null) {
+            p.putAll(System.getProperties());
+        } else {
+            p = System.getProperties();
+        }
+        return p;
+    }
+
+    public JDBCSessionManager() {
+        this.channelName = "test";
+
+        Properties props = readProperties();
+        String host = props.getProperty(SimanticsJDBC.PROP_SIMANTICS_JDBC_HOST, VALUE_SIMANTICS_JDBC_HOST);
+        String port = props.getProperty(SimanticsJDBC.PROP_SIMANTICS_JDBC_PORT, "" + VALUE_SIMANTICS_JDBC_PORT);
+        String database = props.getProperty(SimanticsJDBC.PROP_SIMANTICS_JDBC_DATABASE, VALUE_SIMANTICS_JDBC_DATABASE);
+        String user = props.getProperty(SimanticsJDBC.PROP_SIMANTICS_JDBC_USER, VALUE_SIMANTICS_JDBC_USER);
+        String password = props.getProperty(SimanticsJDBC.PROP_SIMANTICS_JDBC_PASSWORD, VALUE_SIMANTICS_JDBC_PASSWORD);
+
+        try {
+            int portNumber = Integer.valueOf(port);
+
+            dataSource = new PGDataSource();
+            dataSource.setHost(host);
+            dataSource.setPort(portNumber);
+            dataSource.setDatabase(database);
+            dataSource.setUser(user);
+            dataSource.setPassword(password);
+
+            this.connection = dataSource.getConnection();
+            this.listener = new PGNotificationListener() {
+                @Override
+                public void notification(int processId, String channelName, String payload) {
+                    if (LOGGER.isDebugEnabled())
+                        LOGGER.debug("Received notification from processId={} channelName={} and payload={}", processId, channelName, payload);
+                    Simantics.getSession().asyncRequest(new ReadRequest() {
+                        @Override
+                        public void run(ReadGraph graph) throws DatabaseException {
+                            for (String realmId : INSTANCE.getRealms()) {
+                                try {
+                                    JDBCRealm jdbcRealm = (JDBCRealm) INSTANCE.getOrCreateRealm(graph, realmId);
+                                    jdbcRealm.getNodeManager().refreshVariable(new JDBCNode(payload));
+                                } catch (DatabaseException e) {
+                                    LOGGER.error("Could not refresh variable in realm {} with payload {}", realmId, payload, e);
+                                }
+                            }
+                        }
+                    });
+                }
+            };
+            createTable();
+            init();
+        } catch (SQLException e) {
+            LOGGER.error("Could not initialize JDBCSessionManager!", e);
+        }
+    }
+    
+    private void createTable() throws SQLException {
+        Statement statement = connection.createStatement();
+        statement.execute("CREATE TABLE IF NOT EXISTS simantics_table (key VARCHAR UNIQUE, value JSON)");
+        statement.close();
+    }
+
+    protected void init() throws SQLException {
+        Statement statement = connection.createStatement();
+        statement.execute("LISTEN " + this.channelName);
+        statement.close();
+        ((PGConnection) connection).addNotificationListener(this.listener);
+    }
+
+    protected void destroy() throws SQLException {
+        try (PGConnection connection = (PGConnection) dataSource.getConnection()) {
+            Statement statement = connection.createStatement();
+            statement.execute("UNLISTEN " + this.channelName);
+            statement.close();
+        }
+    }
+
+
+    @Override
+    protected JDBCNodeManagerSupport createEngine(ReadGraph graph, String id) throws DatabaseException {
+        return new JDBCNodeManagerSupport(id, this.dataSource, this.channelName);
+    }
+
+    @Override
+    protected StandardRealm<JDBCNode, JDBCNodeManagerSupport> createRealm(JDBCNodeManagerSupport engine, String id) {
+        return new JDBCRealm(engine, id);
+    }
+
+    public static void setValue(ReadGraph graph, String id, String key, Object value) throws DatabaseException, NodeManagerException, BindingException, InterruptedException {
+        JDBCRealm realm = (JDBCRealm) INSTANCE.getOrCreateRealm(graph, id);
+        realm.asyncExec(() -> {
+            try {
+                realm.getNodeManager().setValue(new JDBCNode(key), key, value, Bindings.OBJECT);
+            } catch (NodeManagerException | BindingException e) {
+                LOGGER.error("Could not set value {} for {}", value, key, e);
+            }
+        });
+    }
+
+    public static NodeSupport<?> nodeSupport(ReadGraph graph, String sessionName) throws DatabaseException {
+        return INSTANCE.getOrCreateNodeSupport(graph, sessionName);
+    }
+
+    public static Object getValue(ReadGraph graph, String uri, String key) throws InterruptedException, DatabaseException {
+        JDBCRealm realm = (JDBCRealm) INSTANCE.getOrCreateRealm(graph, uri);
+        return realm.syncExec(new Function<Object, Object>() {
+
+            @Override
+            public Object apply(Object t) {
+                try {
+                    return realm.getNodeManager().getValue(new JDBCNode(key), key).getValue();
+                } catch (NodeManagerException e) {
+                    LOGGER.error("Could not get value for {}", key, e);
+                    return null;
+                }
+            }
+        });
+    }
+
+}
diff --git a/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCVariableBuilder.java b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCVariableBuilder.java
new file mode 100644 (file)
index 0000000..c633fd7
--- /dev/null
@@ -0,0 +1,21 @@
+package org.simantics.jdbc.variable;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.NodeManagerVariableBuilder;
+import org.simantics.db.layer0.variable.NodeSupport;
+
+public class JDBCVariableBuilder extends NodeManagerVariableBuilder {
+
+    @Override
+    protected NodeSupport<?> getNodeSupport(ReadGraph graph, String sessionName) throws DatabaseException {
+        return JDBCSessionManager.nodeSupport(graph, sessionName);
+    }
+
+    @Override
+    protected Object getRoot(ReadGraph graph, NodeSupport<?> support, String sessionName) throws DatabaseException {
+        JDBCNodeManager manager = (JDBCNodeManager) support.manager;
+        return manager.getRoot();
+    }
+
+}
diff --git a/features/org.simantics.jdbc.feature/.project b/features/org.simantics.jdbc.feature/.project
new file mode 100644 (file)
index 0000000..0d18720
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.simantics.jdbc.feature</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.pde.FeatureBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.FeatureNature</nature>
+       </natures>
+</projectDescription>
diff --git a/features/org.simantics.jdbc.feature/build.properties b/features/org.simantics.jdbc.feature/build.properties
new file mode 100644 (file)
index 0000000..64f93a9
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/features/org.simantics.jdbc.feature/feature.xml b/features/org.simantics.jdbc.feature/feature.xml
new file mode 100644 (file)
index 0000000..66b53fa
--- /dev/null
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.simantics.jdbc.feature"
+      label="Simantics JDBC Feature"
+      version="1.0.0.qualifier">
+
+   <description url="http://www.example.com/description">
+      [Enter Feature Description here.]
+   </description>
+
+   <copyright url="http://www.example.com/copyright">
+      [Enter Copyright Description here.]
+   </copyright>
+
+   <license url="http://www.example.com/license">
+      [Enter License Description here.]
+   </license>
+
+   <plugin
+         id="org.simantics.jdbc"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.simantics.simulator.toolkit"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.simantics.simulator.toolkit.db"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.simantics.simulator"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.simantics.jdbc.ontology"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.simantics.jdbc.simupedia.sharedlibrary"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
index 87df82cfe3b4d8cf4ca3c32215da0b98eb68b932..1001743bea6053ea8d92b908d96a1ce132358ce4 100644 (file)
                                         <Import-Package>!sun.misc.*,*;resolution:=optional</Import-Package>
                                     </instructions>
                                 </artifact>
+                                <artifact>
+                                    <id>com.impossibl.pgjdbc-ng:pgjdbc-ng:0.7.1</id>
+                                    <source>true</source>
+                                    <transitive>false</transitive>
+                                </artifact>
                             </artifacts>
                         </configuration>
                     </execution>
index 7306e4e4a004f5b68fe0a23a4f268a991d5bab20..aa58f2419da579a697bb5845ed5a6e642fcd2af5 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="Eclipse Oxygen" sequenceNumber="1536132639">
+<target name="Eclipse Oxygen" sequenceNumber="1536132640">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="true" includeSource="true" includeConfigurePhase="false" type="InstallableUnit">
       <unit id="com.google.guava" version="21.0.0.v20170206-1425"/>
       <unit id="org.slf4j.api.source" version="1.7.25.b001"/>
       <unit id="org.supercsv" version="2.4.0"/>
       <unit id="org.supercsv.source" version="2.4.0"/>
+      <unit id="pgjdbc-ng" version="0.7.1"/>
+      <unit id="pgjdbc-ng.source" version="0.7.1"/>
       <unit id="stax2-api" version="3.1.4"/>
       <unit id="stax2-api.source" version="3.1.4"/>
       <repository location="http://www.simantics.org/download/master/external-components/maven"/>
index 50ce5ca5b3fa052c2a47de2ceac9e0a917bab397..2875cbbd2780d0ebea6028e17ad2fc1b5585053c 100644 (file)
@@ -354,6 +354,8 @@ location "http://www.simantics.org/download/master/external-components/maven" {
        org.slf4j.api.source
        org.supercsv
        org.supercsv.source
+       pgjdbc-ng
+       pgjdbc-ng.source
        stax2-api
        stax2-api.source
 }
index eb9605a93ab36f3178ecf6468eb1f8951a7031e0..4853e0c4400c47174324db5d02c06fc3e33314ab 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="Simantics 1.36.0" sequenceNumber="1535957308">
+<target name="Simantics 1.36.0" sequenceNumber="1535957309">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="true" includeSource="true" includeConfigurePhase="false" type="InstallableUnit">
       <unit id="com.google.guava" version="21.0.0.v20170206-1425"/>
       <unit id="org.slf4j.api.source" version="1.7.25.b001"/>
       <unit id="org.supercsv" version="2.4.0"/>
       <unit id="org.supercsv.source" version="2.4.0"/>
+      <unit id="pgjdbc-ng" version="0.7.1"/>
+      <unit id="pgjdbc-ng.source" version="0.7.1"/>
       <unit id="stax2-api" version="3.1.4"/>
       <unit id="stax2-api.source" version="3.1.4"/>
       <repository location="http://www.simantics.org/download/master/external-components/maven"/>