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; } }