From c758b43751ff59edd81ab85ea7bd320446824d99 Mon Sep 17 00:00:00 2001 From: jsimomaa Date: Sat, 30 Jun 2018 15:31:02 +0300 Subject: [PATCH] Simantics JDBC testing with access over Variable/StandardNodeManager gitlab #125 Change-Id: I8dc2cab179e941dca25a2683c1d4f444cb2ae904 --- .../org.simantics.jdbc.ontology/.classpath | 7 + bundles/org.simantics.jdbc.ontology/.project | 34 +++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../META-INF/MANIFEST.MF | 12 + .../build.properties | 5 + .../graph/JDBC.pgraph | 10 + .../simantics/jdbc/ontology/Activator.java | 30 +++ bundles/org.simantics.jdbc/.classpath | 7 + bundles/org.simantics.jdbc/.project | 34 +++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../org.simantics.jdbc/META-INF/MANIFEST.MF | 24 ++ bundles/org.simantics.jdbc/adapters.xml | 6 + bundles/org.simantics.jdbc/build.properties | 6 + bundles/org.simantics.jdbc/jdbc.properties | 5 + .../src/org/simantics/jdbc/SimanticsJDBC.java | 45 ++++ .../simantics/jdbc/internal/Activator.java | 18 ++ .../org/simantics/jdbc/variable/JDBCNode.java | 41 ++++ .../jdbc/variable/JDBCNodeManager.java | 21 ++ .../jdbc/variable/JDBCNodeManagerSupport.java | 123 +++++++++++ .../simantics/jdbc/variable/JDBCRealm.java | 28 +++ .../jdbc/variable/JDBCSessionManager.java | 208 ++++++++++++++++++ .../jdbc/variable/JDBCVariableBuilder.java | 21 ++ features/org.simantics.jdbc.feature/.project | 17 ++ .../build.properties | 1 + .../org.simantics.jdbc.feature/feature.xml | 61 +++++ .../org.simantics.sdk.build.p2.site/pom.xml | 5 + ...imantics.sdk.build.targetdefinition.target | 4 +- ...g.simantics.sdk.build.targetdefinition.tpd | 2 + .../simantics.target | 4 +- 29 files changed, 791 insertions(+), 2 deletions(-) create mode 100644 bundles/org.simantics.jdbc.ontology/.classpath create mode 100644 bundles/org.simantics.jdbc.ontology/.project create mode 100644 bundles/org.simantics.jdbc.ontology/.settings/org.eclipse.jdt.core.prefs create mode 100644 bundles/org.simantics.jdbc.ontology/META-INF/MANIFEST.MF create mode 100644 bundles/org.simantics.jdbc.ontology/build.properties create mode 100644 bundles/org.simantics.jdbc.ontology/graph/JDBC.pgraph create mode 100644 bundles/org.simantics.jdbc.ontology/src/org/simantics/jdbc/ontology/Activator.java create mode 100644 bundles/org.simantics.jdbc/.classpath create mode 100644 bundles/org.simantics.jdbc/.project create mode 100644 bundles/org.simantics.jdbc/.settings/org.eclipse.jdt.core.prefs create mode 100644 bundles/org.simantics.jdbc/META-INF/MANIFEST.MF create mode 100644 bundles/org.simantics.jdbc/adapters.xml create mode 100644 bundles/org.simantics.jdbc/build.properties create mode 100644 bundles/org.simantics.jdbc/jdbc.properties create mode 100644 bundles/org.simantics.jdbc/src/org/simantics/jdbc/SimanticsJDBC.java create mode 100644 bundles/org.simantics.jdbc/src/org/simantics/jdbc/internal/Activator.java create mode 100644 bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNode.java create mode 100644 bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManager.java create mode 100644 bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManagerSupport.java create mode 100644 bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCRealm.java create mode 100644 bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCSessionManager.java create mode 100644 bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCVariableBuilder.java create mode 100644 features/org.simantics.jdbc.feature/.project create mode 100644 features/org.simantics.jdbc.feature/build.properties create mode 100644 features/org.simantics.jdbc.feature/feature.xml diff --git a/bundles/org.simantics.jdbc.ontology/.classpath b/bundles/org.simantics.jdbc.ontology/.classpath new file mode 100644 index 000000000..eca7bdba8 --- /dev/null +++ b/bundles/org.simantics.jdbc.ontology/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.jdbc.ontology/.project b/bundles/org.simantics.jdbc.ontology/.project new file mode 100644 index 000000000..6668cd10e --- /dev/null +++ b/bundles/org.simantics.jdbc.ontology/.project @@ -0,0 +1,34 @@ + + + org.simantics.jdbc.ontology + + + + + + org.simantics.graph.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.simantics.graph.nature + + 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 index 000000000..0c68a61dc --- /dev/null +++ b/bundles/org.simantics.jdbc.ontology/.settings/org.eclipse.jdt.core.prefs @@ -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 index 000000000..69dae10ab --- /dev/null +++ b/bundles/org.simantics.jdbc.ontology/META-INF/MANIFEST.MF @@ -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 index 000000000..e85b630a0 --- /dev/null +++ b/bundles/org.simantics.jdbc.ontology/build.properties @@ -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 index 000000000..432e10c88 --- /dev/null +++ b/bundles/org.simantics.jdbc.ontology/graph/JDBC.pgraph @@ -0,0 +1,10 @@ +L0 = +SEL = + +JDBC = : L0.Ontology + @L0.new + L0.HasResourceClass "org.simantics.jdbc.JDBCResource" + +JDBC.Session -- JDBC.Session.hasValue --> L0.Value + + + + + + diff --git a/bundles/org.simantics.jdbc/.project b/bundles/org.simantics.jdbc/.project new file mode 100644 index 000000000..3af9a2f8f --- /dev/null +++ b/bundles/org.simantics.jdbc/.project @@ -0,0 +1,34 @@ + + + org.simantics.jdbc + + + + + + org.simantics.graph.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.simantics.graph.nature + + 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 index 000000000..0c68a61dc --- /dev/null +++ b/bundles/org.simantics.jdbc/.settings/org.eclipse.jdt.core.prefs @@ -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 index 000000000..9aff2f490 --- /dev/null +++ b/bundles/org.simantics.jdbc/META-INF/MANIFEST.MF @@ -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 index 000000000..800ee1461 --- /dev/null +++ b/bundles/org.simantics.jdbc/adapters.xml @@ -0,0 +1,6 @@ + + + + + + \ 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 index 000000000..0dc8dce86 --- /dev/null +++ b/bundles/org.simantics.jdbc/build.properties @@ -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 index 000000000..7f23859a1 --- /dev/null +++ b/bundles/org.simantics.jdbc/jdbc.properties @@ -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 index 000000000..ac287e6e4 --- /dev/null +++ b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/SimanticsJDBC.java @@ -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 index 000000000..0f9da36d7 --- /dev/null +++ b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/internal/Activator.java @@ -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 index 000000000..54f5d3947 --- /dev/null +++ b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNode.java @@ -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 index 000000000..fd2585211 --- /dev/null +++ b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManager.java @@ -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 { + + public JDBCNodeManager(StandardRealm realm, JDBCNode root) { + super(realm, root); + } + + @Override + public Set 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 index 000000000..b1c16a6df --- /dev/null +++ b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCNodeManagerSupport.java @@ -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 { + + 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 getChildren(JDBCNode node) { + return Collections.emptyMap(); + } + + @Override + public Map getProperties(JDBCNode node) { + HashMap 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 index 000000000..9a6f4ab15 --- /dev/null +++ b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCRealm.java @@ -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 { + + private static final Logger LOGGER = LoggerFactory.getLogger(JDBCRealm.class); + + protected JDBCRealm(JDBCNodeManagerSupport engine, String id) { + super(engine, id); + } + + @Override + protected StandardNodeManager 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 index 000000000..f6a6077e4 --- /dev/null +++ b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCSessionManager.java @@ -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 { + + 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 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() { + + @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 index 000000000..c633fd71e --- /dev/null +++ b/bundles/org.simantics.jdbc/src/org/simantics/jdbc/variable/JDBCVariableBuilder.java @@ -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 index 000000000..0d1872076 --- /dev/null +++ b/features/org.simantics.jdbc.feature/.project @@ -0,0 +1,17 @@ + + + org.simantics.jdbc.feature + + + + + + org.eclipse.pde.FeatureBuilder + + + + + + org.eclipse.pde.FeatureNature + + diff --git a/features/org.simantics.jdbc.feature/build.properties b/features/org.simantics.jdbc.feature/build.properties new file mode 100644 index 000000000..64f93a9f0 --- /dev/null +++ b/features/org.simantics.jdbc.feature/build.properties @@ -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 index 000000000..66b53fac8 --- /dev/null +++ b/features/org.simantics.jdbc.feature/feature.xml @@ -0,0 +1,61 @@ + + + + + [Enter Feature Description here.] + + + + [Enter Copyright Description here.] + + + + [Enter License Description here.] + + + + + + + + + + + + + + + diff --git a/releng/org.simantics.sdk.build.p2.site/pom.xml b/releng/org.simantics.sdk.build.p2.site/pom.xml index 87df82cfe..1001743be 100644 --- a/releng/org.simantics.sdk.build.p2.site/pom.xml +++ b/releng/org.simantics.sdk.build.p2.site/pom.xml @@ -514,6 +514,11 @@ !sun.misc.*,*;resolution:=optional + + com.impossibl.pgjdbc-ng:pgjdbc-ng:0.7.1 + true + false + diff --git a/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target b/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target index 7306e4e4a..aa58f2419 100644 --- a/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target +++ b/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target @@ -1,7 +1,7 @@ - + @@ -355,6 +355,8 @@ + + diff --git a/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd b/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd index 50ce5ca5b..2875cbbd2 100644 --- a/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd +++ b/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd @@ -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 } diff --git a/releng/org.simantics.sdk.build.targetdefinition/simantics.target b/releng/org.simantics.sdk.build.targetdefinition/simantics.target index eb9605a93..4853e0c44 100644 --- a/releng/org.simantics.sdk.build.targetdefinition/simantics.target +++ b/releng/org.simantics.sdk.build.targetdefinition/simantics.target @@ -1,7 +1,7 @@ - + @@ -364,6 +364,8 @@ + + -- 2.43.2