From 0cc1ef21bfd27c11f7b296e19502f9105b79ff5f Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Tue, 14 Sep 2021 20:38:24 -0500 Subject: [PATCH 01/18] basic up --- addons/common/persistence/jdbc/pom.xml | 1 + .../jdbc/JDBCProcessInstances.java | 70 +++++++++---------- .../src/main/resources/sql/create_tables.sql | 10 --- .../resources/sql/create_tables_Oracle.sql | 6 ++ .../sql/create_tables_PostgreSQL.sql | 7 ++ .../src/main/resources/sql/exists_tables.sql | 3 - kogito-test-utils/pom.xml | 6 ++ 7 files changed, 55 insertions(+), 48 deletions(-) delete mode 100644 addons/common/persistence/jdbc/src/main/resources/sql/create_tables.sql create mode 100644 addons/common/persistence/jdbc/src/main/resources/sql/create_tables_Oracle.sql create mode 100644 addons/common/persistence/jdbc/src/main/resources/sql/create_tables_PostgreSQL.sql delete mode 100644 addons/common/persistence/jdbc/src/main/resources/sql/exists_tables.sql diff --git a/addons/common/persistence/jdbc/pom.xml b/addons/common/persistence/jdbc/pom.xml index e8af3567330..9957e897667 100644 --- a/addons/common/persistence/jdbc/pom.xml +++ b/addons/common/persistence/jdbc/pom.xml @@ -7,6 +7,7 @@ kogito-addons-persistence-jdbc Kogito :: Add-Ons :: Persistence :: JDBC + JDBC based persistence for Kogito diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java index 8632446aab3..30c4c3c5527 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -49,6 +50,8 @@ public class JDBCProcessInstances implements MutableProcessInstances { private static final String PAYLOAD = "payload"; + private static final List SUPPORTED_DBS = List.of("PostgreSQL", "Oracle"); + private static final Logger LOGGER = LoggerFactory.getLogger(JDBCProcessInstances.class); private final Process process; @@ -80,10 +83,24 @@ private void init() { return; } - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(getQueryFromFile("exists_tables"))) { + try (Connection connection = dataSource.getConnection()) { + final DatabaseMetaData metaData = connection.getMetaData(); + final String dbType = metaData.getDatabaseProductName(); + if (!SUPPORTED_DBS.contains(dbType)) { + throw new Exception("Database (" + dbType + ")" + "not suported"); + } + final String[] types = { "TABLE" }; + ResultSet tables = metaData.getTables(null, null, "%process_instances%", types); + boolean exist = false; + while (tables.next()) { + LOGGER.debug("Found process_instance table"); + exist = true; + } - createTable(connection, statement); + if (!exist) { + LOGGER.info("dynamically creating process_instances table"); + createTable(connection, dbType); + } } catch (Exception e) { //not break the execution flow in case of any missing permission for db application user, for instance. @@ -92,46 +109,29 @@ private void init() { } } - private void createTable(Connection connection, PreparedStatement statement) { - boolean result = false; - try (ResultSet resultSet = statement.executeQuery()) { - if (resultSet.next()) { - result = Optional.ofNullable(resultSet.getBoolean("exists")) - .filter(Boolean.FALSE::equals) - .map(e -> { - try { - PreparedStatement prepareStatement = connection.prepareStatement(getQueryFromFile("create_tables")); - return prepareStatement.execute(); - } catch (SQLException e1) { - LOGGER.error("Error creating process_instances table", e1); - } - return false; - }) - .orElseGet(() -> { - LOGGER.info("Table process_instances already exists."); - return false; - }); - } - - } catch (Exception e) { - throw uncheckedException(e, "Error creating process_instances table"); - } - - if (result) { + private void createTable(final Connection connection, final String dbType) { + try { + final String query = getQueryFromFile(dbType, "create_tables"); + LOGGER.error(query); + PreparedStatement prepareStatement = connection.prepareStatement(query); + prepareStatement.execute(); LOGGER.info("DDL successfully done for ProcessInstance"); - } else { - LOGGER.info("DDL executed with no changes for ProcessInstance"); + } catch (SQLException e1) { + LOGGER.error("Error creating process_instances table", e1); } } - private String getQueryFromFile(String scriptName) { - - try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(String.format("sql/%s.sql", scriptName))) { + private String getQueryFromFile(final String dbType, final String scriptName) { + final String fileName = String.format("sql/%s_%s.sql", scriptName, dbType); + try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { + if (stream == null) { + throw new Exception(); + } byte[] buffer = new byte[stream.available()]; stream.read(buffer); return new String(buffer); } catch (Exception e) { - throw uncheckedException(e, "Error reading query script file %s", scriptName); + throw uncheckedException(e, "Error reading query script file %s", fileName); } } diff --git a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables.sql b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables.sql deleted file mode 100644 index 4d3eaf0fecc..00000000000 --- a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE process_instances(id uuid NOT NULL, - payload bytea NOT NULL, - process_id character varying NOT NULL, - version bigint, - CONSTRAINT process_instances_pkey PRIMARY KEY (id) - ); -CREATE INDEX idx_process_instances_process_id ON process_instances - ( - process_id - ); \ No newline at end of file diff --git a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_Oracle.sql b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_Oracle.sql new file mode 100644 index 00000000000..4acaf3d3bc0 --- /dev/null +++ b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_Oracle.sql @@ -0,0 +1,6 @@ +CREATE TABLE process_instances( + id char(36) NOT NULL, + payload blob NOT NULL, + process_id varchar2(4000) NOT NULL, + version number(19), + CONSTRAINT process_instances_pkey PRIMARY KEY (id)) \ No newline at end of file diff --git a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_PostgreSQL.sql b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_PostgreSQL.sql new file mode 100644 index 00000000000..16408d1d4f1 --- /dev/null +++ b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_PostgreSQL.sql @@ -0,0 +1,7 @@ +CREATE TABLE process_instances( + id uuid NOT NULL, + payload bytea NOT NULL, + process_id character varying NOT NULL, + version bigint, + CONSTRAINT process_instances_pkey PRIMARY KEY (id)); +CREATE INDEX idx_process_instances_process_id ON process_instances (process_id); \ No newline at end of file diff --git a/addons/common/persistence/jdbc/src/main/resources/sql/exists_tables.sql b/addons/common/persistence/jdbc/src/main/resources/sql/exists_tables.sql deleted file mode 100644 index 84cc939b13a..00000000000 --- a/addons/common/persistence/jdbc/src/main/resources/sql/exists_tables.sql +++ /dev/null @@ -1,3 +0,0 @@ -SELECT EXISTS( - SELECT FROM pg_tables WHERE tablename = 'process_instances' - ); \ No newline at end of file diff --git a/kogito-test-utils/pom.xml b/kogito-test-utils/pom.xml index ebb10b3bf1f..1d21ec1546c 100644 --- a/kogito-test-utils/pom.xml +++ b/kogito-test-utils/pom.xml @@ -74,6 +74,12 @@ ${version.org.testcontainers} + + org.testcontainers + oracle-xe + ${version.org.testcontainers} + + org.testcontainers database-commons From 962a7c1e3d62fd55c1e11be5a0f92363cfd1d650 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Tue, 14 Sep 2021 21:43:54 -0500 Subject: [PATCH 02/18] fix issue with table name pattern --- .../jdbc/JDBCProcessInstances.java | 50 ++++++++++++++----- .../resources/sql/create_tables_Oracle.sql | 3 +- .../jdbc/JdbcProcessInstancesIT.java | 3 +- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java index 30c4c3c5527..2fb073d5eeb 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java @@ -50,7 +50,28 @@ public class JDBCProcessInstances implements MutableProcessInstances { private static final String PAYLOAD = "payload"; - private static final List SUPPORTED_DBS = List.of("PostgreSQL", "Oracle"); + enum DatabaseType { + POSTGRES("PostgreSQL", "process_instances"), + ORACLE("Oracle", "PROCESS_INSTANCES"); + + private final String dbIdentifier; + private final String tableNamePattern; + + DatabaseType(final String dbIdentifier, final String tableNamePattern) { + this.dbIdentifier = dbIdentifier; + this.tableNamePattern = tableNamePattern; + } + + public static DatabaseType create(final String dbIdentifier) { + if ("Oracle".equals(dbIdentifier)) { + return ORACLE; + } else if ("PostgreSQL".equals(dbIdentifier)) { + return POSTGRES; + } + return null; + } + + } private static final Logger LOGGER = LoggerFactory.getLogger(JDBCProcessInstances.class); @@ -85,12 +106,13 @@ private void init() { try (Connection connection = dataSource.getConnection()) { final DatabaseMetaData metaData = connection.getMetaData(); - final String dbType = metaData.getDatabaseProductName(); - if (!SUPPORTED_DBS.contains(dbType)) { - throw new Exception("Database (" + dbType + ")" + "not suported"); + final String dbProductName = metaData.getDatabaseProductName(); + DatabaseType databaseType = DatabaseType.create(dbProductName); + if (databaseType == null) { + throw new Exception("Database (" + dbProductName + ") not suported"); } final String[] types = { "TABLE" }; - ResultSet tables = metaData.getTables(null, null, "%process_instances%", types); + ResultSet tables = metaData.getTables(null, null, databaseType.tableNamePattern, types); boolean exist = false; while (tables.next()) { LOGGER.debug("Found process_instance table"); @@ -99,7 +121,7 @@ private void init() { if (!exist) { LOGGER.info("dynamically creating process_instances table"); - createTable(connection, dbType); + createTable(connection, databaseType); } } catch (Exception e) { @@ -109,19 +131,20 @@ private void init() { } } - private void createTable(final Connection connection, final String dbType) { + private void createTable(final Connection connection, final DatabaseType dbType) { try { - final String query = getQueryFromFile(dbType, "create_tables"); - LOGGER.error(query); - PreparedStatement prepareStatement = connection.prepareStatement(query); - prepareStatement.execute(); + final List statements = getQueryFromFile(dbType.dbIdentifier, "create_tables"); + for (String s : statements) { + PreparedStatement prepareStatement = connection.prepareStatement(s.trim()); + prepareStatement.execute(); + } LOGGER.info("DDL successfully done for ProcessInstance"); } catch (SQLException e1) { LOGGER.error("Error creating process_instances table", e1); } } - private String getQueryFromFile(final String dbType, final String scriptName) { + private List getQueryFromFile(final String dbType, final String scriptName) { final String fileName = String.format("sql/%s_%s.sql", scriptName, dbType); try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { if (stream == null) { @@ -129,7 +152,8 @@ private String getQueryFromFile(final String dbType, final String scriptName) { } byte[] buffer = new byte[stream.available()]; stream.read(buffer); - return new String(buffer); + String[] statments = new String(buffer).split(";"); + return List.of(statments); } catch (Exception e) { throw uncheckedException(e, "Error reading query script file %s", fileName); } diff --git a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_Oracle.sql b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_Oracle.sql index 4acaf3d3bc0..a986cf401d2 100644 --- a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_Oracle.sql +++ b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_Oracle.sql @@ -3,4 +3,5 @@ CREATE TABLE process_instances( payload blob NOT NULL, process_id varchar2(4000) NOT NULL, version number(19), - CONSTRAINT process_instances_pkey PRIMARY KEY (id)) \ No newline at end of file + CONSTRAINT process_instances_pkey PRIMARY KEY (id)); +CREATE INDEX idx_process_instances_process_id ON process_instances (process_id); \ No newline at end of file diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java index 5798b8fb08f..8dfc4500871 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java @@ -40,8 +40,7 @@ class JdbcProcessInstancesIT extends TestHelper { void testBasicTaskFlow() { BpmnProcess process = createProcess(null, "BPMN2-UserTask.bpmn2", false); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections - .singletonMap("test", - "test"))); + .singletonMap("test", "test"))); processInstance.start(); assertThat(processInstance.status()).isEqualTo(STATE_ACTIVE); From 6c006ed1d16f033d221755d30128df93c9e6ff48 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Thu, 16 Sep 2021 10:16:42 -0500 Subject: [PATCH 03/18] create repository --- .../jdbc/JDBCProcessInstances.java | 146 +++-------------- .../persistence/jdbc/OracleRepository.java | 147 ++++++++++++++++++ .../persistence/jdbc/PostgresRepository.java | 142 +++++++++++++++++ .../kogito/persistence/jdbc/Repository.java | 52 +++++++ 4 files changed, 363 insertions(+), 124 deletions(-) create mode 100644 addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/OracleRepository.java create mode 100644 addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/PostgresRepository.java create mode 100644 addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java index 2fb073d5eeb..42b2c51bf6d 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java @@ -21,9 +21,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -46,9 +44,8 @@ public class JDBCProcessInstances implements MutableProcessInstances { - private static final String VERSION = "version"; - - private static final String PAYLOAD = "payload"; + static final String PAYLOAD = "payload"; + static final String VERSION = "version"; enum DatabaseType { POSTGRES("PostgreSQL", "process_instances"), @@ -70,7 +67,6 @@ public static DatabaseType create(final String dbIdentifier) { } return null; } - } private static final Logger LOGGER = LoggerFactory.getLogger(JDBCProcessInstances.class); @@ -81,13 +77,7 @@ public static DatabaseType create(final String dbIdentifier) { private final DataSource dataSource; private final boolean lock; - private static final String FIND_ALL = "SELECT payload FROM process_instances WHERE process_id = ?"; - private static final String FIND_BY_ID = "SELECT payload, version FROM process_instances WHERE id = ?"; - private static final String INSERT = "INSERT INTO process_instances (id, payload, process_id, version) VALUES (?, ?, ?, ?)"; - private static final String UPDATE = "UPDATE process_instances SET payload = ? WHERE id = ?"; - private static final String UPDATE_WITH_LOCK = "UPDATE process_instances SET payload = ?, version = ? WHERE id = ? and version = ?"; - private static final String DELETE = "DELETE FROM process_instances WHERE id = ?"; - private static final String COUNT = "SELECT COUNT(id) FROM process_instances WHERE process_id = ?"; + private Repository repository; public JDBCProcessInstances(Process process, DataSource dataSource, boolean autoDDL, boolean lock) { this.dataSource = dataSource; @@ -111,6 +101,15 @@ private void init() { if (databaseType == null) { throw new Exception("Database (" + dbProductName + ") not suported"); } + switch (databaseType) { + case ORACLE: + repository = new OracleRepository(); + break; + case POSTGRES: + repository = new PostgresRepository(); + break; + } + final String[] types = { "TABLE" }; ResultSet tables = metaData.getTables(null, null, databaseType.tableNamePattern, types); boolean exist = false; @@ -168,89 +167,39 @@ public boolean exists(String id) { @Override public void create(String id, ProcessInstance instance) { if (isActive(instance)) { - insertInternal(UUID.fromString(id), marshaller.marshallProcessInstance(instance)); + repository.insertInternal(dataSource, process.id(), UUID.fromString(id), marshaller.marshallProcessInstance(instance)); } disconnect(instance); } - private void insertInternal(UUID id, byte[] payload) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(INSERT)) { - statement.setObject(1, id); - statement.setBytes(2, payload); - statement.setString(3, process.id()); - statement.setLong(4, 1L); - statement.executeUpdate(); - } catch (Exception e) { - throw uncheckedException(e, "Error inserting process instance %s", id); - } - } - @SuppressWarnings("unchecked") @Override public void update(String id, ProcessInstance instance) { if (isActive(instance)) { if (lock) { - boolean isUpdated = updateWithLock(UUID.fromString(id), marshaller.marshallProcessInstance(instance), instance.version()); + boolean isUpdated = repository.updateWithLock(dataSource, UUID.fromString(id), marshaller.marshallProcessInstance(instance), instance.version()); if (!isUpdated) { throw uncheckedException(null, "The document with ID: %s was updated or deleted by other request.", id); } } else { - updateInternal(UUID.fromString(id), marshaller.marshallProcessInstance(instance)); + repository.updateInternal(dataSource, UUID.fromString(id), marshaller.marshallProcessInstance(instance)); } } disconnect(instance); } - private void updateInternal(UUID id, byte[] payload) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(UPDATE)) { - statement.setBytes(1, payload); - statement.setObject(2, id); - statement.executeUpdate(); - } catch (Exception e) { - throw uncheckedException(e, "Error updating process instance %s", id); - } - } - - private boolean updateWithLock(UUID id, byte[] payload, long version) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(UPDATE_WITH_LOCK)) { - statement.setBytes(1, payload); - statement.setLong(2, version + 1); - statement.setObject(3, id); - statement.setLong(4, version); - int count = statement.executeUpdate(); - return count == 1; - } catch (Exception e) { - throw uncheckedException(e, "Error updating process instance %s", id); - } - } - @Override public void remove(String id) { - boolean isDeleted = deleteInternal(UUID.fromString(id)); + boolean isDeleted = repository.deleteInternal(dataSource, UUID.fromString(id)); if (lock && !isDeleted) { throw uncheckedException(null, "The document with ID: %s was deleted by other request.", id); } } - private boolean deleteInternal(UUID id) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(DELETE)) { - statement.setObject(1, id); - int count = statement.executeUpdate(); - return count == 1; - } catch (Exception e) { - throw uncheckedException(e, "Error deleting process instance %s", id); - } - - } - @Override public Optional findById(String id, ProcessInstanceReadMode mode) { ProcessInstance instance = null; - Map map = findByIdInternal(UUID.fromString(id)); + Map map = repository.findByIdInternal(dataSource, UUID.fromString(id)); if (map.containsKey(PAYLOAD)) { byte[] b = (byte[]) map.get(PAYLOAD); instance = mode == MUTABLE ? marshaller.unmarshallProcessInstance(b, process) @@ -263,50 +212,14 @@ public Optional findById(String id, ProcessInstanceReadMode mod @Override public Collection values(ProcessInstanceReadMode mode) { - return findAllInternal().stream().map(b -> mode == MUTABLE ? marshaller.unmarshallProcessInstance(b, process) : marshaller.unmarshallReadOnlyProcessInstance(b, process)) + return repository.findAllInternal(dataSource, process.id()).stream() + .map(b -> mode == MUTABLE ? marshaller.unmarshallProcessInstance(b, process) : marshaller.unmarshallReadOnlyProcessInstance(b, process)) .collect(Collectors.toList()); } - private Map findByIdInternal(UUID id) { - Map result = new HashMap<>(); - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(FIND_BY_ID)) { - statement.setObject(1, id); - try (ResultSet resultSet = statement.executeQuery()) { - if (resultSet.next()) { - Optional b = Optional.ofNullable(resultSet.getBytes(PAYLOAD)); - if (b.isPresent()) { - result.put(PAYLOAD, b.get()); - } - result.put(VERSION, resultSet.getLong(VERSION)); - return result; - } - } - } catch (Exception e) { - throw uncheckedException(e, "Error finding process instance %s", id); - } - return result; - } - - private List findAllInternal() { - List result = new ArrayList<>(); - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(FIND_ALL)) { - statement.setString(1, process.id()); - try (ResultSet resultSet = statement.executeQuery()) { - while (resultSet.next()) { - result.add(resultSet.getBytes(PAYLOAD)); - } - } - return result; - } catch (Exception e) { - throw uncheckedException(e, "Error finding all process instances, for processId %s", process.id()); - } - } - @Override public Integer size() { - return countInternal().intValue(); + return repository.countInternal(dataSource, process.id()).intValue(); } @Override @@ -314,24 +227,9 @@ public boolean lock() { return this.lock; } - private Long countInternal() { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(COUNT)) { - statement.setString(1, process.id()); - try (ResultSet resultSet = statement.executeQuery()) { - if (resultSet.next()) { - return resultSet.getLong("count"); - } - } - } catch (Exception e) { - throw uncheckedException(e, "Error counting process instances, for processId %s", process.id()); - } - return 0l; - } - private void disconnect(ProcessInstance instance) { Supplier supplier = () -> { - Map map = findByIdInternal(UUID.fromString(instance.id())); + Map map = repository.findByIdInternal(dataSource, UUID.fromString(instance.id())); ((AbstractProcessInstance) instance).setVersion((Long) map.get(VERSION)); return (byte[]) map.get(PAYLOAD); }; @@ -341,4 +239,4 @@ private void disconnect(ProcessInstance instance) { private RuntimeException uncheckedException(Exception ex, String message, Object... param) { return new RuntimeException(String.format(message, param), ex); } -} \ No newline at end of file +} diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/OracleRepository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/OracleRepository.java new file mode 100644 index 00000000000..eb7ff956757 --- /dev/null +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/OracleRepository.java @@ -0,0 +1,147 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.kie.kogito.persistence.jdbc; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import javax.sql.DataSource; + +import static org.kie.kogito.persistence.jdbc.JDBCProcessInstances.PAYLOAD; +import static org.kie.kogito.persistence.jdbc.JDBCProcessInstances.VERSION; + +public class OracleRepository extends Repository { + + //changed + @Override + public void insertInternal(DataSource dataSource, String processId, UUID id, byte[] payload) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(INSERT)) { + statement.setString(1, id.toString()); + statement.setBytes(2, payload); + statement.setString(3, processId); + statement.setLong(4, 1L); + statement.executeUpdate(); + } catch (Exception e) { + throw uncheckedException(e, "Error inserting process instance %s", id); + } + } + + //Changed + @Override + void updateInternal(DataSource dataSource, UUID id, byte[] payload) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(UPDATE)) { + statement.setBytes(1, payload); + statement.setString(2, id.toString()); + statement.executeUpdate(); + } catch (Exception e) { + throw uncheckedException(e, "Error updating process instance %s", id); + } + } + + //changed + @Override + boolean updateWithLock(DataSource dataSource, UUID id, byte[] payload, long version) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(UPDATE_WITH_LOCK)) { + statement.setBytes(1, payload); + statement.setLong(2, version + 1); + statement.setString(3, id.toString()); + statement.setLong(4, version); + int count = statement.executeUpdate(); + return count == 1; + } catch (Exception e) { + throw uncheckedException(e, "Error updating process instance %s", id); + } + } + + //Changed + @Override + boolean deleteInternal(DataSource dataSource, UUID id) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(DELETE)) { + statement.setString(1, id.toString()); + int count = statement.executeUpdate(); + return count == 1; + } catch (Exception e) { + throw uncheckedException(e, "Error deleting process instance %s", id); + } + } + + //Changed + @Override + Map findByIdInternal(DataSource dataSource, UUID id) { + Map result = new HashMap<>(); + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(FIND_BY_ID)) { + statement.setString(1, id.toString()); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + Optional b = Optional.ofNullable(resultSet.getBytes(PAYLOAD)); + if (b.isPresent()) { + result.put(PAYLOAD, b.get()); + } + result.put(VERSION, resultSet.getLong(VERSION)); + return result; + } + } + } catch (Exception e) { + throw uncheckedException(e, "Error finding process instance %s", id); + } + return result; + } + + @Override + List findAllInternal(DataSource dataSource, String processId) { + List result = new ArrayList<>(); + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(FIND_ALL)) { + statement.setString(1, processId); + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + result.add(resultSet.getBytes(PAYLOAD)); + } + } + return result; + } catch (Exception e) { + throw uncheckedException(e, "Error finding all process instances, for processId %s", processId); + } + } + + @Override + Long countInternal(DataSource dataSource, String processId) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(COUNT)) { + statement.setString(1, processId); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + return resultSet.getLong("count"); + } + } + } catch (Exception e) { + throw uncheckedException(e, "Error counting process instances, for processId %s", processId); + } + return 0l; + } +} diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/PostgresRepository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/PostgresRepository.java new file mode 100644 index 00000000000..7bb4dff31ec --- /dev/null +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/PostgresRepository.java @@ -0,0 +1,142 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.kie.kogito.persistence.jdbc; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import javax.sql.DataSource; + +import static org.kie.kogito.persistence.jdbc.JDBCProcessInstances.PAYLOAD; +import static org.kie.kogito.persistence.jdbc.JDBCProcessInstances.VERSION; + +public class PostgresRepository extends Repository { + + @Override + void insertInternal(DataSource dataSource, String processId, UUID id, byte[] payload) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(INSERT)) { + statement.setObject(1, id); + statement.setBytes(2, payload); + statement.setString(3, processId); + statement.setLong(4, 1L); + statement.executeUpdate(); + } catch (Exception e) { + throw uncheckedException(e, "Error inserting process instance %s", id); + } + } + + @Override + void updateInternal(DataSource dataSource, UUID id, byte[] payload) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(UPDATE)) { + statement.setBytes(1, payload); + statement.setObject(2, id); + statement.executeUpdate(); + } catch (Exception e) { + throw uncheckedException(e, "Error updating process instance %s", id); + } + } + + @Override + boolean updateWithLock(DataSource dataSource, UUID id, byte[] payload, long version) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(UPDATE_WITH_LOCK)) { + statement.setBytes(1, payload); + statement.setLong(2, version + 1); + statement.setObject(3, id); + statement.setLong(4, version); + int count = statement.executeUpdate(); + return count == 1; + } catch (Exception e) { + throw uncheckedException(e, "Error updating process instance %s", id); + } + } + + @Override + boolean deleteInternal(DataSource dataSource, UUID id) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(DELETE)) { + statement.setObject(1, id); + int count = statement.executeUpdate(); + return count == 1; + } catch (Exception e) { + throw uncheckedException(e, "Error deleting process instance %s", id); + } + } + + @Override + Map findByIdInternal(DataSource dataSource, UUID id) { + Map result = new HashMap<>(); + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(FIND_BY_ID)) { + statement.setObject(1, id); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + Optional b = Optional.ofNullable(resultSet.getBytes(PAYLOAD)); + if (b.isPresent()) { + result.put(PAYLOAD, b.get()); + } + result.put(VERSION, resultSet.getLong(VERSION)); + return result; + } + } + } catch (Exception e) { + throw uncheckedException(e, "Error finding process instance %s", id); + } + return result; + } + + @Override + List findAllInternal(DataSource dataSource, String processId) { + List result = new ArrayList<>(); + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(FIND_ALL)) { + statement.setString(1, processId); + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + result.add(resultSet.getBytes(PAYLOAD)); + } + } + return result; + } catch (Exception e) { + throw uncheckedException(e, "Error finding all process instances, for processId %s", processId); + } + } + + @Override + Long countInternal(DataSource dataSource, String processId) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement(COUNT)) { + statement.setString(1, processId); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + return resultSet.getLong("count"); + } + } + } catch (Exception e) { + throw uncheckedException(e, "Error counting process instances, for processId %s", processId); + } + return 0l; + } +} diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java new file mode 100644 index 00000000000..50fdd7784be --- /dev/null +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.kie.kogito.persistence.jdbc; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.sql.DataSource; + +public abstract class Repository { + + static final String INSERT = "INSERT INTO process_instances (id, payload, process_id, version) VALUES (?, ?, ?, ?)"; + static final String FIND_ALL = "SELECT payload FROM process_instances WHERE process_id = ?"; + static final String FIND_BY_ID = "SELECT payload, version FROM process_instances WHERE id = ?"; + static final String UPDATE = "UPDATE process_instances SET payload = ? WHERE id = ?"; + static final String UPDATE_WITH_LOCK = "UPDATE process_instances SET payload = ?, version = ? WHERE id = ? and version = ?"; + static final String DELETE = "DELETE FROM process_instances WHERE id = ?"; + static final String COUNT = "SELECT COUNT(id) FROM process_instances WHERE process_id = ?"; + + abstract void insertInternal(DataSource dataSource, String processId, UUID id, byte[] payload); + + abstract void updateInternal(DataSource dataSource, UUID id, byte[] payload); + + abstract boolean updateWithLock(DataSource dataSource, UUID id, byte[] payload, long version); + + abstract boolean deleteInternal(DataSource dataSource, UUID id); + + abstract Map findByIdInternal(DataSource dataSource, UUID id); + + abstract List findAllInternal(DataSource dataSource, String processId); + + abstract Long countInternal(DataSource dataSource, String processId); + + protected RuntimeException uncheckedException(Exception ex, String message, Object... param) { + return new RuntimeException(String.format(message, param), ex); + } + +} From b46a8a1811f00332cd7458a9588a1dd3ec672bc7 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Fri, 17 Sep 2021 16:08:33 -0500 Subject: [PATCH 04/18] merge postgres/oracle repository into Generic --- addons/common/persistence/jdbc/pom.xml | 3 +- .../kogito/persistence/jdbc/FileLoader.java | 38 +++++ ...Repository.java => GenericRepository.java} | 82 +++++++++- .../jdbc/JDBCProcessInstances.java | 97 +----------- .../persistence/jdbc/PostgresRepository.java | 142 ------------------ .../kogito/persistence/jdbc/Repository.java | 6 +- .../sql/create_tables_PostgreSQL.sql | 2 +- 7 files changed, 127 insertions(+), 243 deletions(-) create mode 100644 addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java rename addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/{OracleRepository.java => GenericRepository.java} (64%) delete mode 100644 addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/PostgresRepository.java diff --git a/addons/common/persistence/jdbc/pom.xml b/addons/common/persistence/jdbc/pom.xml index 9957e897667..f53bc600602 100644 --- a/addons/common/persistence/jdbc/pom.xml +++ b/addons/common/persistence/jdbc/pom.xml @@ -7,7 +7,6 @@ kogito-addons-persistence-jdbc Kogito :: Add-Ons :: Persistence :: JDBC - JDBC based persistence for Kogito @@ -81,4 +80,4 @@ - \ No newline at end of file + diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java new file mode 100644 index 00000000000..db1dae278a0 --- /dev/null +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.kie.kogito.persistence.jdbc; + +import java.io.InputStream; +import java.util.List; + +public class FileLoader { + + static List getQueryFromFile(final String dbType, final String scriptName) { + final String fileName = String.format("sql/%s_%s.sql", scriptName, dbType); + try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { + if (stream == null) { + throw new Exception(); + } + byte[] buffer = new byte[stream.available()]; + stream.read(buffer); + String[] statments = new String(buffer).split(";"); + return List.of(statments); + } catch (Exception e) { + throw new RuntimeException(String.format("Error reading query script file %s", fileName), e); + } + } + +} diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/OracleRepository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java similarity index 64% rename from addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/OracleRepository.java rename to addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java index eb7ff956757..e7b5d16bd9e 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/OracleRepository.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java @@ -16,8 +16,10 @@ package org.kie.kogito.persistence.jdbc; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -27,14 +29,83 @@ import javax.sql.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import static org.kie.kogito.persistence.jdbc.JDBCProcessInstances.PAYLOAD; import static org.kie.kogito.persistence.jdbc.JDBCProcessInstances.VERSION; -public class OracleRepository extends Repository { +public class GenericRepository extends Repository { + + private static final Logger LOGGER = LoggerFactory.getLogger(GenericRepository.class); + + private enum DatabaseType { + POSTGRES("PostgreSQL", "process_instances"), + ORACLE("Oracle", "PROCESS_INSTANCES"); + + private final String dbIdentifier; + private final String tableNamePattern; + + DatabaseType(final String dbIdentifier, final String tableNamePattern) { + this.dbIdentifier = dbIdentifier; + this.tableNamePattern = tableNamePattern; + } + + public static DatabaseType create(final String dbIdentifier) { + if ("Oracle".equals(dbIdentifier)) { + return ORACLE; + } else if ("PostgreSQL".equals(dbIdentifier)) { + return POSTGRES; + } else { + var msg = String.format("Unrecognized DB (%s), defaulting to postgres", dbIdentifier); + LOGGER.warn(msg); + return POSTGRES; + } + } + } + + private DatabaseType getDataBaseType(Connection connection) throws SQLException { + final DatabaseMetaData metaData = connection.getMetaData(); + final String dbProductName = metaData.getDatabaseProductName(); + return DatabaseType.create(dbProductName); + } - //changed @Override - public void insertInternal(DataSource dataSource, String processId, UUID id, byte[] payload) { + boolean tableExists(DataSource dataSource) { + try (Connection connection = dataSource.getConnection()) { + DatabaseType databaseType = getDataBaseType(connection); + final DatabaseMetaData metaData = connection.getMetaData(); + final String[] types = { "TABLE" }; + ResultSet tables = metaData.getTables(null, null, databaseType.tableNamePattern, types); + while (tables.next()) { + LOGGER.debug("Found process_instance table"); + return true; + } + return false; + } catch (SQLException e) { + var msg = "Failed to read table metadata"; + throw new RuntimeException(msg); + } + } + + @Override + void createTable(DataSource dataSource) { + try (Connection connection = dataSource.getConnection()) { + DatabaseType databaseType = getDataBaseType(connection); + final List statements = FileLoader.getQueryFromFile(databaseType.dbIdentifier, "create_tables"); + for (String s : statements) { + PreparedStatement prepareStatement = connection.prepareStatement(s.trim()); + prepareStatement.execute(); + } + LOGGER.info("DDL successfully done for ProcessInstance"); + } catch (SQLException e) { + var msg = "Error creating process_instances table, the database should be configured properly before starting the application"; + throw new RuntimeException(msg); + } + } + + @Override + void insertInternal(DataSource dataSource, String processId, UUID id, byte[] payload) { try (Connection connection = dataSource.getConnection(); PreparedStatement statement = connection.prepareStatement(INSERT)) { statement.setString(1, id.toString()); @@ -47,7 +118,6 @@ public void insertInternal(DataSource dataSource, String processId, UUID id, byt } } - //Changed @Override void updateInternal(DataSource dataSource, UUID id, byte[] payload) { try (Connection connection = dataSource.getConnection(); @@ -60,7 +130,6 @@ void updateInternal(DataSource dataSource, UUID id, byte[] payload) { } } - //changed @Override boolean updateWithLock(DataSource dataSource, UUID id, byte[] payload, long version) { try (Connection connection = dataSource.getConnection(); @@ -76,7 +145,6 @@ boolean updateWithLock(DataSource dataSource, UUID id, byte[] payload, long vers } } - //Changed @Override boolean deleteInternal(DataSource dataSource, UUID id) { try (Connection connection = dataSource.getConnection(); @@ -89,7 +157,6 @@ boolean deleteInternal(DataSource dataSource, UUID id) { } } - //Changed @Override Map findByIdInternal(DataSource dataSource, UUID id) { Map result = new HashMap<>(); @@ -144,4 +211,5 @@ Long countInternal(DataSource dataSource, String processId) { } return 0l; } + } diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java index 42b2c51bf6d..d59d0c0d127 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/JDBCProcessInstances.java @@ -15,14 +15,7 @@ */ package org.kie.kogito.persistence.jdbc; -import java.io.InputStream; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -47,28 +40,6 @@ public class JDBCProcessInstances implements MutableProcessInstances { static final String PAYLOAD = "payload"; static final String VERSION = "version"; - enum DatabaseType { - POSTGRES("PostgreSQL", "process_instances"), - ORACLE("Oracle", "PROCESS_INSTANCES"); - - private final String dbIdentifier; - private final String tableNamePattern; - - DatabaseType(final String dbIdentifier, final String tableNamePattern) { - this.dbIdentifier = dbIdentifier; - this.tableNamePattern = tableNamePattern; - } - - public static DatabaseType create(final String dbIdentifier) { - if ("Oracle".equals(dbIdentifier)) { - return ORACLE; - } else if ("PostgreSQL".equals(dbIdentifier)) { - return POSTGRES; - } - return null; - } - } - private static final Logger LOGGER = LoggerFactory.getLogger(JDBCProcessInstances.class); private final Process process; @@ -76,8 +47,7 @@ public static DatabaseType create(final String dbIdentifier) { private final boolean autoDDL; private final DataSource dataSource; private final boolean lock; - - private Repository repository; + private final Repository repository; public JDBCProcessInstances(Process process, DataSource dataSource, boolean autoDDL, boolean lock) { this.dataSource = dataSource; @@ -85,6 +55,7 @@ public JDBCProcessInstances(Process process, DataSource dataSource, boolean a this.autoDDL = autoDDL; this.lock = lock; this.marshaller = ProcessInstanceMarshallerService.newBuilder().withDefaultObjectMarshallerStrategies().build(); + this.repository = new GenericRepository(); init(); } @@ -93,68 +64,14 @@ private void init() { LOGGER.debug("Auto DDL is disabled, do not running initializer scripts"); return; } - - try (Connection connection = dataSource.getConnection()) { - final DatabaseMetaData metaData = connection.getMetaData(); - final String dbProductName = metaData.getDatabaseProductName(); - DatabaseType databaseType = DatabaseType.create(dbProductName); - if (databaseType == null) { - throw new Exception("Database (" + dbProductName + ") not suported"); - } - switch (databaseType) { - case ORACLE: - repository = new OracleRepository(); - break; - case POSTGRES: - repository = new PostgresRepository(); - break; - } - - final String[] types = { "TABLE" }; - ResultSet tables = metaData.getTables(null, null, databaseType.tableNamePattern, types); - boolean exist = false; - while (tables.next()) { - LOGGER.debug("Found process_instance table"); - exist = true; - } - - if (!exist) { - LOGGER.info("dynamically creating process_instances table"); - createTable(connection, databaseType); - } - - } catch (Exception e) { - //not break the execution flow in case of any missing permission for db application user, for instance. - LOGGER.error("Error creating process_instances table, the database should be configured properly before " + - "starting the application", e); - } - } - - private void createTable(final Connection connection, final DatabaseType dbType) { try { - final List statements = getQueryFromFile(dbType.dbIdentifier, "create_tables"); - for (String s : statements) { - PreparedStatement prepareStatement = connection.prepareStatement(s.trim()); - prepareStatement.execute(); - } - LOGGER.info("DDL successfully done for ProcessInstance"); - } catch (SQLException e1) { - LOGGER.error("Error creating process_instances table", e1); - } - } - - private List getQueryFromFile(final String dbType, final String scriptName) { - final String fileName = String.format("sql/%s_%s.sql", scriptName, dbType); - try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { - if (stream == null) { - throw new Exception(); + if (!repository.tableExists(dataSource)) { + LOGGER.info("dynamically creating process_instances table"); + repository.createTable(dataSource); } - byte[] buffer = new byte[stream.available()]; - stream.read(buffer); - String[] statments = new String(buffer).split(";"); - return List.of(statments); } catch (Exception e) { - throw uncheckedException(e, "Error reading query script file %s", fileName); + // not break the execution flow in case of any missing permission for db application user, for instance. + LOGGER.error(e.getMessage(), e); } } diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/PostgresRepository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/PostgresRepository.java deleted file mode 100644 index 7bb4dff31ec..00000000000 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/PostgresRepository.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.kie.kogito.persistence.jdbc; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -import javax.sql.DataSource; - -import static org.kie.kogito.persistence.jdbc.JDBCProcessInstances.PAYLOAD; -import static org.kie.kogito.persistence.jdbc.JDBCProcessInstances.VERSION; - -public class PostgresRepository extends Repository { - - @Override - void insertInternal(DataSource dataSource, String processId, UUID id, byte[] payload) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(INSERT)) { - statement.setObject(1, id); - statement.setBytes(2, payload); - statement.setString(3, processId); - statement.setLong(4, 1L); - statement.executeUpdate(); - } catch (Exception e) { - throw uncheckedException(e, "Error inserting process instance %s", id); - } - } - - @Override - void updateInternal(DataSource dataSource, UUID id, byte[] payload) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(UPDATE)) { - statement.setBytes(1, payload); - statement.setObject(2, id); - statement.executeUpdate(); - } catch (Exception e) { - throw uncheckedException(e, "Error updating process instance %s", id); - } - } - - @Override - boolean updateWithLock(DataSource dataSource, UUID id, byte[] payload, long version) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(UPDATE_WITH_LOCK)) { - statement.setBytes(1, payload); - statement.setLong(2, version + 1); - statement.setObject(3, id); - statement.setLong(4, version); - int count = statement.executeUpdate(); - return count == 1; - } catch (Exception e) { - throw uncheckedException(e, "Error updating process instance %s", id); - } - } - - @Override - boolean deleteInternal(DataSource dataSource, UUID id) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(DELETE)) { - statement.setObject(1, id); - int count = statement.executeUpdate(); - return count == 1; - } catch (Exception e) { - throw uncheckedException(e, "Error deleting process instance %s", id); - } - } - - @Override - Map findByIdInternal(DataSource dataSource, UUID id) { - Map result = new HashMap<>(); - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(FIND_BY_ID)) { - statement.setObject(1, id); - try (ResultSet resultSet = statement.executeQuery()) { - if (resultSet.next()) { - Optional b = Optional.ofNullable(resultSet.getBytes(PAYLOAD)); - if (b.isPresent()) { - result.put(PAYLOAD, b.get()); - } - result.put(VERSION, resultSet.getLong(VERSION)); - return result; - } - } - } catch (Exception e) { - throw uncheckedException(e, "Error finding process instance %s", id); - } - return result; - } - - @Override - List findAllInternal(DataSource dataSource, String processId) { - List result = new ArrayList<>(); - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(FIND_ALL)) { - statement.setString(1, processId); - try (ResultSet resultSet = statement.executeQuery()) { - while (resultSet.next()) { - result.add(resultSet.getBytes(PAYLOAD)); - } - } - return result; - } catch (Exception e) { - throw uncheckedException(e, "Error finding all process instances, for processId %s", processId); - } - } - - @Override - Long countInternal(DataSource dataSource, String processId) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(COUNT)) { - statement.setString(1, processId); - try (ResultSet resultSet = statement.executeQuery()) { - if (resultSet.next()) { - return resultSet.getLong("count"); - } - } - } catch (Exception e) { - throw uncheckedException(e, "Error counting process instances, for processId %s", processId); - } - return 0l; - } -} diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java index 50fdd7784be..183a17da07f 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java @@ -21,7 +21,7 @@ import javax.sql.DataSource; -public abstract class Repository { +abstract class Repository { static final String INSERT = "INSERT INTO process_instances (id, payload, process_id, version) VALUES (?, ?, ?, ?)"; static final String FIND_ALL = "SELECT payload FROM process_instances WHERE process_id = ?"; @@ -31,6 +31,10 @@ public abstract class Repository { static final String DELETE = "DELETE FROM process_instances WHERE id = ?"; static final String COUNT = "SELECT COUNT(id) FROM process_instances WHERE process_id = ?"; + abstract boolean tableExists(DataSource dataSource); + + abstract void createTable(DataSource dataSource); + abstract void insertInternal(DataSource dataSource, String processId, UUID id, byte[] payload); abstract void updateInternal(DataSource dataSource, UUID id, byte[] payload); diff --git a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_PostgreSQL.sql b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_PostgreSQL.sql index 16408d1d4f1..5126606ba40 100644 --- a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_PostgreSQL.sql +++ b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_PostgreSQL.sql @@ -1,5 +1,5 @@ CREATE TABLE process_instances( - id uuid NOT NULL, + id character(36) NOT NULL, payload bytea NOT NULL, process_id character varying NOT NULL, version bigint, From e1c437d591f56f342098586f046eb7b1eb1510fe Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Sat, 18 Sep 2021 17:08:48 -0500 Subject: [PATCH 05/18] add oracledb testing --- .../persistence/jdbc/GenericRepository.java | 7 +- .../kogito/persistence/jdbc/Repository.java | 2 +- .../jdbc/OracleProcessInstancesIT.java | 106 ++++++++++++++++++ ...T.java => PostgresProcessInstancesIT.java} | 8 +- ...> PostgresProcessInstancesWithLockIT.java} | 8 +- .../org/kie/persistence/jdbc/TestHelper.java | 82 ++++++-------- .../jdbc/TestProcessInstancesFactory.java | 46 ++++++++ kogito-test-utils/pom.xml | 6 + .../KogitoOracleSqlContainer.java | 81 +++++++++++++ 9 files changed, 289 insertions(+), 57 deletions(-) create mode 100644 addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/OracleProcessInstancesIT.java rename addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/{JdbcProcessInstancesIT.java => PostgresProcessInstancesIT.java} (92%) rename addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/{JdbcProcessInstancesWithLockIT.java => PostgresProcessInstancesWithLockIT.java} (90%) create mode 100644 addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestProcessInstancesFactory.java create mode 100644 kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java index e7b5d16bd9e..6ad5c89939a 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java @@ -94,8 +94,9 @@ void createTable(DataSource dataSource) { DatabaseType databaseType = getDataBaseType(connection); final List statements = FileLoader.getQueryFromFile(databaseType.dbIdentifier, "create_tables"); for (String s : statements) { - PreparedStatement prepareStatement = connection.prepareStatement(s.trim()); - prepareStatement.execute(); + try (PreparedStatement prepareStatement = connection.prepareStatement(s.trim())) { + prepareStatement.execute(); + } } LOGGER.info("DDL successfully done for ProcessInstance"); } catch (SQLException e) { @@ -141,7 +142,7 @@ boolean updateWithLock(DataSource dataSource, UUID id, byte[] payload, long vers int count = statement.executeUpdate(); return count == 1; } catch (Exception e) { - throw uncheckedException(e, "Error updating process instance %s", id); + throw uncheckedException(e, "Error updating with lock process instance %s", id); } } diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java index 183a17da07f..e9bd8bc244f 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/Repository.java @@ -29,7 +29,7 @@ abstract class Repository { static final String UPDATE = "UPDATE process_instances SET payload = ? WHERE id = ?"; static final String UPDATE_WITH_LOCK = "UPDATE process_instances SET payload = ?, version = ? WHERE id = ? and version = ?"; static final String DELETE = "DELETE FROM process_instances WHERE id = ?"; - static final String COUNT = "SELECT COUNT(id) FROM process_instances WHERE process_id = ?"; + static final String COUNT = "SELECT COUNT(id) as count FROM process_instances WHERE process_id = ?"; abstract boolean tableExists(DataSource dataSource); diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/OracleProcessInstancesIT.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/OracleProcessInstancesIT.java new file mode 100644 index 00000000000..eb2cf2e880d --- /dev/null +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/OracleProcessInstancesIT.java @@ -0,0 +1,106 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.kie.persistence.jdbc; + +import java.util.Collections; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.kie.kogito.persistence.jdbc.JDBCProcessInstances; +import org.kie.kogito.process.ProcessInstance; +import org.kie.kogito.process.WorkItem; +import org.kie.kogito.process.bpmn2.BpmnProcess; +import org.kie.kogito.process.bpmn2.BpmnProcessInstance; +import org.kie.kogito.process.bpmn2.BpmnVariables; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.kie.kogito.internal.process.runtime.KogitoProcessInstance.STATE_ACTIVE; +import static org.kie.kogito.internal.process.runtime.KogitoProcessInstance.STATE_COMPLETED; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +class OracleProcessInstancesIT extends TestHelper { + + @Test + void testBasicTaskFlow() { + var factory = new TestProcessInstancesFactory(ORACLE_DATA_SOURCE, false); + BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); + ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections + .singletonMap("test", "test"))); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(STATE_ACTIVE); + assertThat(processInstance.description()).isEqualTo("User Task"); + + JDBCProcessInstances processInstances = (JDBCProcessInstances) process.instances(); + assertThat(processInstances.size()).isOne(); + assertThat(processInstances.exists(processInstance.id())).isTrue(); + verify(processInstances).create(any(), any()); + + String testVar = (String) processInstance.variables().get("test"); + assertThat(testVar).isEqualTo("test"); + + assertThat(processInstance.description()).isEqualTo("User Task"); + + assertThat(process.instances().values().iterator().next().workItems(securityPolicy)).hasSize(1); + + WorkItem workItem = processInstance.workItems(securityPolicy).get(0); + assertThat(workItem).isNotNull(); + assertThat(workItem.getParameters()).containsEntry("ActorId", "john"); + processInstance.completeWorkItem(workItem.getId(), null, securityPolicy); + assertThat(processInstance.status()).isEqualTo(STATE_COMPLETED); + + processInstances = (JDBCProcessInstances) process.instances(); + verify(processInstances, times(2)).remove(processInstance.id()); + assertThat(processInstances.size()).isZero(); + assertThat(process.instances().values()).isEmpty(); + } + + @Test + void testBasicFlow() { + var factory = new TestProcessInstancesFactory(ORACLE_DATA_SOURCE, false); + BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); + ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections + .singletonMap("test", + "test"))); + processInstance.start(); + + JDBCProcessInstances processInstances = (JDBCProcessInstances) process.instances(); + assertThat(processInstances.size()).isOne(); + Optional foundOne = processInstances.findById(processInstance.id()); + BpmnProcessInstance instanceOne = (BpmnProcessInstance) foundOne.get(); + processInstances.update(processInstance.id(), instanceOne); + + assertThat(processInstances.size()).isOne(); + assertThat(processInstances.exists(TEST_ID)).isFalse(); + Optional foundTwo = processInstances.findById(TEST_ID); + assertThat(foundTwo).isEmpty(); + + processInstances.remove(processInstance.id()); + assertThat(processInstances.size()).isZero(); + assertThat(process.instances().values()).isEmpty(); + } + + @Test + void testException() { + BpmnProcess process = configure(false); + JDBCProcessInstances processInstances = (JDBCProcessInstances) process.instances(); + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> processInstances.findById(TEST_ID)); + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> processInstances.remove(TEST_ID)); + } +} diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesIT.java similarity index 92% rename from addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java rename to addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesIT.java index 8dfc4500871..bb469f5292d 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesIT.java @@ -34,11 +34,12 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -class JdbcProcessInstancesIT extends TestHelper { +class PostgresProcessInstancesIT extends TestHelper { @Test void testBasicTaskFlow() { - BpmnProcess process = createProcess(null, "BPMN2-UserTask.bpmn2", false); + var factory = new TestProcessInstancesFactory(PG_DATA_SOURCE, false); + BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections .singletonMap("test", "test"))); processInstance.start(); @@ -72,7 +73,8 @@ void testBasicTaskFlow() { @Test void testBasicFlow() { - BpmnProcess process = createProcess(null, "BPMN2-UserTask.bpmn2", false); + var factory = new TestProcessInstancesFactory(PG_DATA_SOURCE, false); + BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections .singletonMap("test", "test"))); diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesWithLockIT.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesWithLockIT.java similarity index 90% rename from addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesWithLockIT.java rename to addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesWithLockIT.java index 7c9543d13e9..1442efd8b45 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesWithLockIT.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesWithLockIT.java @@ -28,11 +28,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -class JdbcProcessInstancesWithLockIT extends TestHelper { +class PostgresProcessInstancesWithLockIT extends TestHelper { @Test public void testUpdate() { - BpmnProcess process = createProcess(null, "BPMN2-UserTask.bpmn2", true); + var factory = new TestProcessInstancesFactory(PG_DATA_SOURCE, true); + BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections.singletonMap("test", "test"))); processInstance.start(); @@ -62,7 +63,8 @@ public void testUpdate() { @Test public void testRemove() { - BpmnProcess process = createProcess(null, "BPMN2-UserTask.bpmn2", true); + var factory = new TestProcessInstancesFactory(PG_DATA_SOURCE, true); + BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections.singletonMap("test", "test"))); processInstance.start(); diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java index a769e6cc01e..73e3dafb98a 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java @@ -16,6 +16,8 @@ package org.kie.persistence.jdbc; +import java.sql.SQLException; + import javax.sql.DataSource; import org.drools.core.io.impl.ClassPathResource; @@ -23,98 +25,84 @@ import org.junit.jupiter.api.BeforeAll; import org.kie.kogito.auth.IdentityProviders; import org.kie.kogito.auth.SecurityPolicy; -import org.kie.kogito.persistence.KogitoProcessInstancesFactory; -import org.kie.kogito.persistence.jdbc.JDBCProcessInstances; -import org.kie.kogito.process.Process; import org.kie.kogito.process.ProcessConfig; import org.kie.kogito.process.ProcessInstanceReadMode; import org.kie.kogito.process.bpmn2.BpmnProcess; +import org.kie.kogito.testcontainers.KogitoOracleSqlContainer; import org.kie.kogito.testcontainers.KogitoPostgreSqlContainer; import org.postgresql.ds.PGSimpleDataSource; +import org.testcontainers.containers.OracleContainer; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import static org.mockito.Mockito.spy; +import oracle.jdbc.pool.OracleDataSource; @Testcontainers public class TestHelper { @Container - final static KogitoPostgreSqlContainer container = new KogitoPostgreSqlContainer(); - private static DataSource ds; + final static KogitoPostgreSqlContainer PG_CONTAINER = new KogitoPostgreSqlContainer(); + + @Container + final static KogitoOracleSqlContainer ORACLE_CONTAINER = new KogitoOracleSqlContainer(); + + static DataSource PG_DATA_SOURCE; + static DataSource ORACLE_DATA_SOURCE; public static SecurityPolicy securityPolicy = SecurityPolicy.of(IdentityProviders.of("john")); public static final String TEST_ID = "02ac3854-46ee-42b7-8b63-5186c9889d96"; - private boolean enableLock; - @BeforeAll public static void startContainerAndPublicPortIsAvailable() { - container.start(); - ds = getDataSource(container); + PG_CONTAINER.start(); + PG_DATA_SOURCE = getPGDataSource(PG_CONTAINER); + + ORACLE_CONTAINER.start(); + ORACLE_DATA_SOURCE = getOracleDataSource(ORACLE_CONTAINER); } @AfterAll public static void close() { - container.stop(); + PG_CONTAINER.stop(); + ORACLE_CONTAINER.stop(); } - public static BpmnProcess createProcess(ProcessConfig config, String fileName, boolean lock) { + public static BpmnProcess createProcess(TestProcessInstancesFactory factory, ProcessConfig config, String fileName) { BpmnProcess process = BpmnProcess.from(config, new ClassPathResource(fileName)).get(0); - process.setProcessInstancesFactory(getFactory(lock)); + process.setProcessInstancesFactory(factory); process.configure(); process.instances().values(ProcessInstanceReadMode.MUTABLE).forEach(p -> p.abort()); return process; } - private static JDBCProcessInstancesFactory getFactory(boolean lock) { - TestHelper t = new TestHelper(); - t.setEnableLock(lock); - return t.new JDBCProcessInstancesFactory(ds); - } - - private static DataSource getDataSource(final PostgreSQLContainer postgreSQLContainer) { - + private static DataSource getPGDataSource(final PostgreSQLContainer postgreSQLContainer) { PGSimpleDataSource ds = new PGSimpleDataSource(); - - // DataSource initialization ds.setUrl(postgreSQLContainer.getJdbcUrl()); ds.setUser(postgreSQLContainer.getUsername()); ds.setPassword(postgreSQLContainer.getPassword()); return ds; } + private static DataSource getOracleDataSource(final OracleContainer oracleContainer) { + try { + OracleDataSource ds = new OracleDataSource(); + ds.setURL(oracleContainer.getJdbcUrl()); + ds.setUser(oracleContainer.getUsername()); + ds.setPassword(oracleContainer.getPassword()); + return ds; + } catch (SQLException e) { + throw new RuntimeException("Failed to create oracle datasource"); + } + } + public static BpmnProcess configure(boolean lock) { BpmnProcess process = BpmnProcess.from(new ClassPathResource("BPMN2-UserTask-Script.bpmn2")).get(0); - TestHelper t = new TestHelper(); - t.setEnableLock(lock); - process.setProcessInstancesFactory(t.new JDBCProcessInstancesFactory(null)); + process.setProcessInstancesFactory(new TestProcessInstancesFactory(null, lock)); process.configure(); return process; } - public void setEnableLock(boolean enableLock) { - this.enableLock = enableLock; - } - - private class JDBCProcessInstancesFactory extends KogitoProcessInstancesFactory { - - public JDBCProcessInstancesFactory(DataSource dataSource) { - super(dataSource, true); - } - - @Override - public JDBCProcessInstances createProcessInstances(Process process) { - JDBCProcessInstances instances = spy(super.createProcessInstances(process)); - return instances; - } - - @Override - public boolean lock() { - return enableLock; - } - } } diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestProcessInstancesFactory.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestProcessInstancesFactory.java new file mode 100644 index 00000000000..cb007159cf5 --- /dev/null +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestProcessInstancesFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.kie.persistence.jdbc; + +import javax.sql.DataSource; + +import org.kie.kogito.persistence.KogitoProcessInstancesFactory; +import org.kie.kogito.persistence.jdbc.JDBCProcessInstances; +import org.kie.kogito.process.Process; + +import static org.mockito.Mockito.spy; + +public class TestProcessInstancesFactory extends KogitoProcessInstancesFactory { + + private boolean enableLock; + + public TestProcessInstancesFactory(DataSource dataSource, boolean lock) { + super(dataSource, true); + this.enableLock = lock; + } + + @Override + public JDBCProcessInstances createProcessInstances(Process process) { + JDBCProcessInstances instances = spy(super.createProcessInstances(process)); + return instances; + } + + @Override + public boolean lock() { + return enableLock; + } + +} diff --git a/kogito-test-utils/pom.xml b/kogito-test-utils/pom.xml index 1d21ec1546c..4814fa26ba0 100644 --- a/kogito-test-utils/pom.xml +++ b/kogito-test-utils/pom.xml @@ -78,6 +78,12 @@ org.testcontainers oracle-xe ${version.org.testcontainers} + compile + + + com.oracle.database.jdbc + ojdbc11 + 21.3.0.0 diff --git a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java new file mode 100644 index 00000000000..526b667cee5 --- /dev/null +++ b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java @@ -0,0 +1,81 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.kie.kogito.testcontainers; + +import java.text.MessageFormat; +import java.util.function.Consumer; + +import org.kie.kogito.test.resources.TestResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.OracleContainer; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.Slf4jLogConsumer; + +/** + * OracleXE Container for Kogito examples. + */ +public class KogitoOracleSqlContainer extends OracleContainer implements TestResource { + + private static final Logger LOGGER = LoggerFactory.getLogger(KogitoOracleSqlContainer.class); + + public static final String ORACLE_CONNECTION_URI = "kogito.persistence.oracle.connection.uri"; + //Not difined `public` in testcontainers + private static final int ORACLE_PORT = 1521; + + public KogitoOracleSqlContainer() { + super("oracleinanutshell/oracle-xe-11g"); + withLogConsumer(getLogger()); + withLogConsumer(new Slf4jLogConsumer(LOGGER)); + withStartupTimeout(Constants.CONTAINER_START_TIMEOUT); + } + + private Consumer getLogger() { + return f -> System.out.print(f.getUtf8String()); + } + + @Override + public void start() { + super.start(); + LOGGER.info("Oracle server: {}", this.getContainerIpAddress() + ":" + this.getMappedPort(ORACLE_PORT)); + } + + @Override + public int getMappedPort() { + return getMappedPort(ORACLE_PORT); + } + + @Override + public String getResourceName() { + return "oracle"; + } + + public String getReactiveUrl() { + final String connectionTemplate = "oracle://{0}:{1}@{2}:{3}/{4}?search_path={5}"; + final String user = getUsername(); + final String server = getHost(); + final String secret = getPassword(); + final String port = String.valueOf(getMappedPort()); + final String database = getDatabaseName(); + final String schema = "public"; + return MessageFormat.format(connectionTemplate, user, secret, server, port, database, schema); + } + + @Override + public void stop() { + super.stop(); + } +} From 723789fc481fea2792241d774cd0abff1e206a95 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Mon, 20 Sep 2021 09:44:59 -0500 Subject: [PATCH 06/18] use standard testcontainers api --- .../kie/kogito/testcontainers/KogitoOracleSqlContainer.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java index 526b667cee5..a18d8577f19 100644 --- a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java +++ b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java @@ -33,8 +33,6 @@ public class KogitoOracleSqlContainer extends OracleContainer implements TestRes private static final Logger LOGGER = LoggerFactory.getLogger(KogitoOracleSqlContainer.class); public static final String ORACLE_CONNECTION_URI = "kogito.persistence.oracle.connection.uri"; - //Not difined `public` in testcontainers - private static final int ORACLE_PORT = 1521; public KogitoOracleSqlContainer() { super("oracleinanutshell/oracle-xe-11g"); @@ -50,12 +48,12 @@ private Consumer getLogger() { @Override public void start() { super.start(); - LOGGER.info("Oracle server: {}", this.getContainerIpAddress() + ":" + this.getMappedPort(ORACLE_PORT)); + LOGGER.info("Oracle server: {}", this.getContainerIpAddress() + ":" + this.getOraclePort()); } @Override public int getMappedPort() { - return getMappedPort(ORACLE_PORT); + return getOraclePort(); } @Override From c7e49d85d3a17e83726e0c3b9cb555751e389d60 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Mon, 20 Sep 2021 10:46:50 -0500 Subject: [PATCH 07/18] do not duplicate strings --- .../kie/kogito/persistence/jdbc/GenericRepository.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java index 6ad5c89939a..27ec8751efe 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java @@ -51,10 +51,14 @@ private enum DatabaseType { this.tableNamePattern = tableNamePattern; } + String getDbIdentifier() { + return this.dbIdentifier; + } + public static DatabaseType create(final String dbIdentifier) { - if ("Oracle".equals(dbIdentifier)) { + if (ORACLE.getDbIdentifier().equals(dbIdentifier)) { return ORACLE; - } else if ("PostgreSQL".equals(dbIdentifier)) { + } else if (POSTGRES.getDbIdentifier().equals(dbIdentifier)) { return POSTGRES; } else { var msg = String.format("Unrecognized DB (%s), defaulting to postgres", dbIdentifier); From d27fad59cf1e4e19d78073b44ed8aef28a2b99c4 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Mon, 20 Sep 2021 12:18:55 -0500 Subject: [PATCH 08/18] Update addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java Co-authored-by: Daniele Zonca --- .../main/java/org/kie/kogito/persistence/jdbc/FileLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java index db1dae278a0..dfd2d75318c 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java @@ -24,7 +24,7 @@ static List getQueryFromFile(final String dbType, final String scriptNam final String fileName = String.format("sql/%s_%s.sql", scriptName, dbType); try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { if (stream == null) { - throw new Exception(); + throw new IllegalStateException(String.format("Impossible to find %s", fileName); } byte[] buffer = new byte[stream.available()]; stream.read(buffer); From 788de5ed75b495c7346b1f3caa7726e2b3deb840 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Mon, 20 Sep 2021 12:24:01 -0500 Subject: [PATCH 09/18] Update addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java Co-authored-by: Daniele Zonca --- .../main/java/org/kie/kogito/persistence/jdbc/FileLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java index dfd2d75318c..58576dcd2fb 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java @@ -28,8 +28,8 @@ static List getQueryFromFile(final String dbType, final String scriptNam } byte[] buffer = new byte[stream.available()]; stream.read(buffer); - String[] statments = new String(buffer).split(";"); - return List.of(statments); + String[] statements = new String(buffer).split(";"); + return Arrays.asList(statements); } catch (Exception e) { throw new RuntimeException(String.format("Error reading query script file %s", fileName), e); } From 77eac71ff31ba31781d86fd47fb2d55c927ba21c Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Mon, 20 Sep 2021 12:26:35 -0500 Subject: [PATCH 10/18] move oracle version to bom --- kogito-build/kogito-dependencies-bom/pom.xml | 1 + kogito-test-utils/pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kogito-build/kogito-dependencies-bom/pom.xml b/kogito-build/kogito-dependencies-bom/pom.xml index 77985541084..581d7fa6632 100644 --- a/kogito-build/kogito-dependencies-bom/pom.xml +++ b/kogito-build/kogito-dependencies-bom/pom.xml @@ -29,6 +29,7 @@ 4.18.0 2.27.2 3.15.8 + 21.3.0.0 5.0.1 0.2.0 diff --git a/kogito-test-utils/pom.xml b/kogito-test-utils/pom.xml index 4814fa26ba0..a22943a9e78 100644 --- a/kogito-test-utils/pom.xml +++ b/kogito-test-utils/pom.xml @@ -83,7 +83,7 @@ com.oracle.database.jdbc ojdbc11 - 21.3.0.0 + {version.com.oracle.database.jdbc} From 0c0c285e2394683df6f4ff85e0867cca577c5aa9 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Tue, 21 Sep 2021 10:42:30 -0500 Subject: [PATCH 11/18] ParameterizedTest --- addons/common/persistence/jdbc/pom.xml | 6 + .../kogito/persistence/jdbc/FileLoader.java | 4 +- .../persistence/jdbc/GenericRepository.java | 9 +- .../main/resources/sql/create_tables_ansi.sql | 7 ++ .../src/main/resources/sql/runtime_create.sql | 10 -- ...cesIT.java => JdbcProcessInstancesIT.java} | 24 ++-- ...va => JdbcProcessInstancesWithLockIT.java} | 21 ++-- .../jdbc/PostgresProcessInstancesIT.java | 106 ------------------ .../org/kie/persistence/jdbc/TestHelper.java | 5 + kogito-build/kogito-dependencies-bom/pom.xml | 1 + kogito-test-utils/pom.xml | 2 +- .../KogitoPostgreSqlContainer.java | 3 +- 12 files changed, 57 insertions(+), 141 deletions(-) create mode 100644 addons/common/persistence/jdbc/src/main/resources/sql/create_tables_ansi.sql delete mode 100644 addons/common/persistence/jdbc/src/main/resources/sql/runtime_create.sql rename addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/{OracleProcessInstancesIT.java => JdbcProcessInstancesIT.java} (88%) rename addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/{PostgresProcessInstancesWithLockIT.java => JdbcProcessInstancesWithLockIT.java} (87%) delete mode 100644 addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesIT.java diff --git a/addons/common/persistence/jdbc/pom.xml b/addons/common/persistence/jdbc/pom.xml index f53bc600602..eea5dcf32b0 100644 --- a/addons/common/persistence/jdbc/pom.xml +++ b/addons/common/persistence/jdbc/pom.xml @@ -42,6 +42,12 @@ junit-jupiter-engine test + + org.junit.jupiter + junit-jupiter-params + test + + org.assertj assertj-core diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java index 58576dcd2fb..b3a9fb04861 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java @@ -24,12 +24,12 @@ static List getQueryFromFile(final String dbType, final String scriptNam final String fileName = String.format("sql/%s_%s.sql", scriptName, dbType); try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { if (stream == null) { - throw new IllegalStateException(String.format("Impossible to find %s", fileName); + throw new IllegalStateException(String.format("Impossible to find %s", fileName)); } byte[] buffer = new byte[stream.available()]; stream.read(buffer); String[] statements = new String(buffer).split(";"); - return Arrays.asList(statements); + return List.of(statements); } catch (Exception e) { throw new RuntimeException(String.format("Error reading query script file %s", fileName), e); } diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java index 27ec8751efe..e2d367d08fe 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/GenericRepository.java @@ -40,8 +40,9 @@ public class GenericRepository extends Repository { private static final Logger LOGGER = LoggerFactory.getLogger(GenericRepository.class); private enum DatabaseType { - POSTGRES("PostgreSQL", "process_instances"), - ORACLE("Oracle", "PROCESS_INSTANCES"); + ANSI("ansi", "process_instances"), + ORACLE("Oracle", "PROCESS_INSTANCES"), + POSTGRES("PostgreSQL", "process_instances"); private final String dbIdentifier; private final String tableNamePattern; @@ -61,9 +62,9 @@ public static DatabaseType create(final String dbIdentifier) { } else if (POSTGRES.getDbIdentifier().equals(dbIdentifier)) { return POSTGRES; } else { - var msg = String.format("Unrecognized DB (%s), defaulting to postgres", dbIdentifier); + var msg = String.format("Unrecognized DB (%s), defaulting to ansi", dbIdentifier); LOGGER.warn(msg); - return POSTGRES; + return ANSI; } } } diff --git a/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_ansi.sql b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_ansi.sql new file mode 100644 index 00000000000..570c9417df3 --- /dev/null +++ b/addons/common/persistence/jdbc/src/main/resources/sql/create_tables_ansi.sql @@ -0,0 +1,7 @@ +CREATE TABLE process_instances( + id CHAR(36) NOT NULL, + payload BLOB NOT NULL, + process_id VARCHAR(4000) NOT NULL, + version BIGINT(19), + CONSTRAINT process_instances_pkey PRIMARY KEY (id)); +CREATE INDEX idx_process_instances_process_id ON process_instances (process_id); \ No newline at end of file diff --git a/addons/common/persistence/jdbc/src/main/resources/sql/runtime_create.sql b/addons/common/persistence/jdbc/src/main/resources/sql/runtime_create.sql deleted file mode 100644 index 4d3eaf0fecc..00000000000 --- a/addons/common/persistence/jdbc/src/main/resources/sql/runtime_create.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE process_instances(id uuid NOT NULL, - payload bytea NOT NULL, - process_id character varying NOT NULL, - version bigint, - CONSTRAINT process_instances_pkey PRIMARY KEY (id) - ); -CREATE INDEX idx_process_instances_process_id ON process_instances - ( - process_id - ); \ No newline at end of file diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/OracleProcessInstancesIT.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java similarity index 88% rename from addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/OracleProcessInstancesIT.java rename to addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java index eb2cf2e880d..c447a3413ff 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/OracleProcessInstancesIT.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java @@ -18,7 +18,11 @@ import java.util.Collections; import java.util.Optional; -import org.junit.jupiter.api.Test; +import javax.sql.DataSource; + +import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.kie.kogito.persistence.jdbc.JDBCProcessInstances; import org.kie.kogito.process.ProcessInstance; import org.kie.kogito.process.WorkItem; @@ -34,11 +38,12 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -class OracleProcessInstancesIT extends TestHelper { +class JdbcProcessInstancesIT extends TestHelper { - @Test - void testBasicTaskFlow() { - var factory = new TestProcessInstancesFactory(ORACLE_DATA_SOURCE, false); + @ParameterizedTest + @MethodSource("datasources") + void testBasicTaskFlow(DataSource dataSource) { + var factory = new TestProcessInstancesFactory(dataSource, false); BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections .singletonMap("test", "test"))); @@ -71,9 +76,10 @@ void testBasicTaskFlow() { assertThat(process.instances().values()).isEmpty(); } - @Test - void testBasicFlow() { - var factory = new TestProcessInstancesFactory(ORACLE_DATA_SOURCE, false); + @ParameterizedTest + @MethodSource("datasources") + void testBasicFlow(DataSource dataSource) { + var factory = new TestProcessInstancesFactory(dataSource, false); BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections .singletonMap("test", @@ -97,7 +103,7 @@ void testBasicFlow() { } @Test - void testException() { + void testException(DataSource dataSource) { BpmnProcess process = configure(false); JDBCProcessInstances processInstances = (JDBCProcessInstances) process.instances(); assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> processInstances.findById(TEST_ID)); diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesWithLockIT.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesWithLockIT.java similarity index 87% rename from addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesWithLockIT.java rename to addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesWithLockIT.java index 1442efd8b45..de31ce5ad48 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesWithLockIT.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesWithLockIT.java @@ -18,7 +18,10 @@ import java.util.Collections; import java.util.Optional; -import org.junit.jupiter.api.Test; +import javax.sql.DataSource; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.kie.kogito.persistence.jdbc.JDBCProcessInstances; import org.kie.kogito.process.ProcessInstance; import org.kie.kogito.process.bpmn2.BpmnProcess; @@ -28,11 +31,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -class PostgresProcessInstancesWithLockIT extends TestHelper { +class JdbcProcessInstancesWithLockIT extends TestHelper { - @Test - public void testUpdate() { - var factory = new TestProcessInstancesFactory(PG_DATA_SOURCE, true); + @ParameterizedTest + @MethodSource("datasources") + public void testUpdate(DataSource dataSource) { + var factory = new TestProcessInstancesFactory(dataSource, true); BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections.singletonMap("test", "test"))); processInstance.start(); @@ -61,9 +65,10 @@ public void testUpdate() { assertThat(process.instances().values()).isEmpty(); } - @Test - public void testRemove() { - var factory = new TestProcessInstancesFactory(PG_DATA_SOURCE, true); + @ParameterizedTest + @MethodSource("datasources") + public void testRemove(DataSource dataSource) { + var factory = new TestProcessInstancesFactory(dataSource, true); BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections.singletonMap("test", "test"))); processInstance.start(); diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesIT.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesIT.java deleted file mode 100644 index bb469f5292d..00000000000 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/PostgresProcessInstancesIT.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.kie.persistence.jdbc; - -import java.util.Collections; -import java.util.Optional; - -import org.junit.jupiter.api.Test; -import org.kie.kogito.persistence.jdbc.JDBCProcessInstances; -import org.kie.kogito.process.ProcessInstance; -import org.kie.kogito.process.WorkItem; -import org.kie.kogito.process.bpmn2.BpmnProcess; -import org.kie.kogito.process.bpmn2.BpmnProcessInstance; -import org.kie.kogito.process.bpmn2.BpmnVariables; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.kie.kogito.internal.process.runtime.KogitoProcessInstance.STATE_ACTIVE; -import static org.kie.kogito.internal.process.runtime.KogitoProcessInstance.STATE_COMPLETED; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -class PostgresProcessInstancesIT extends TestHelper { - - @Test - void testBasicTaskFlow() { - var factory = new TestProcessInstancesFactory(PG_DATA_SOURCE, false); - BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); - ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections - .singletonMap("test", "test"))); - processInstance.start(); - - assertThat(processInstance.status()).isEqualTo(STATE_ACTIVE); - assertThat(processInstance.description()).isEqualTo("User Task"); - - JDBCProcessInstances processInstances = (JDBCProcessInstances) process.instances(); - assertThat(processInstances.size()).isOne(); - assertThat(processInstances.exists(processInstance.id())).isTrue(); - verify(processInstances).create(any(), any()); - - String testVar = (String) processInstance.variables().get("test"); - assertThat(testVar).isEqualTo("test"); - - assertThat(processInstance.description()).isEqualTo("User Task"); - - assertThat(process.instances().values().iterator().next().workItems(securityPolicy)).hasSize(1); - - WorkItem workItem = processInstance.workItems(securityPolicy).get(0); - assertThat(workItem).isNotNull(); - assertThat(workItem.getParameters()).containsEntry("ActorId", "john"); - processInstance.completeWorkItem(workItem.getId(), null, securityPolicy); - assertThat(processInstance.status()).isEqualTo(STATE_COMPLETED); - - processInstances = (JDBCProcessInstances) process.instances(); - verify(processInstances, times(2)).remove(processInstance.id()); - assertThat(processInstances.size()).isZero(); - assertThat(process.instances().values()).isEmpty(); - } - - @Test - void testBasicFlow() { - var factory = new TestProcessInstancesFactory(PG_DATA_SOURCE, false); - BpmnProcess process = createProcess(factory, null, "BPMN2-UserTask.bpmn2"); - ProcessInstance processInstance = process.createInstance(BpmnVariables.create(Collections - .singletonMap("test", - "test"))); - processInstance.start(); - - JDBCProcessInstances processInstances = (JDBCProcessInstances) process.instances(); - assertThat(processInstances.size()).isOne(); - Optional foundOne = processInstances.findById(processInstance.id()); - BpmnProcessInstance instanceOne = (BpmnProcessInstance) foundOne.get(); - processInstances.update(processInstance.id(), instanceOne); - - assertThat(processInstances.size()).isOne(); - assertThat(processInstances.exists(TEST_ID)).isFalse(); - Optional foundTwo = processInstances.findById(TEST_ID); - assertThat(foundTwo).isEmpty(); - - processInstances.remove(processInstance.id()); - assertThat(processInstances.size()).isZero(); - assertThat(process.instances().values()).isEmpty(); - } - - @Test - void testException() { - BpmnProcess process = configure(false); - JDBCProcessInstances processInstances = (JDBCProcessInstances) process.instances(); - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> processInstances.findById(TEST_ID)); - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> processInstances.remove(TEST_ID)); - } -} diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java index 73e3dafb98a..42d1c4d4090 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java @@ -17,6 +17,7 @@ package org.kie.persistence.jdbc; import java.sql.SQLException; +import java.util.stream.Stream; import javax.sql.DataSource; @@ -50,6 +51,10 @@ public class TestHelper { static DataSource PG_DATA_SOURCE; static DataSource ORACLE_DATA_SOURCE; + public static Stream datasources() { + return Stream.of(ORACLE_DATA_SOURCE, PG_DATA_SOURCE); + } + public static SecurityPolicy securityPolicy = SecurityPolicy.of(IdentityProviders.of("john")); public static final String TEST_ID = "02ac3854-46ee-42b7-8b63-5186c9889d96"; diff --git a/kogito-build/kogito-dependencies-bom/pom.xml b/kogito-build/kogito-dependencies-bom/pom.xml index 581d7fa6632..73d2e3ad85d 100644 --- a/kogito-build/kogito-dependencies-bom/pom.xml +++ b/kogito-build/kogito-dependencies-bom/pom.xml @@ -29,6 +29,7 @@ 4.18.0 2.27.2 3.15.8 + 21.3.0.0 5.0.1 diff --git a/kogito-test-utils/pom.xml b/kogito-test-utils/pom.xml index a22943a9e78..a4db63f07d9 100644 --- a/kogito-test-utils/pom.xml +++ b/kogito-test-utils/pom.xml @@ -83,7 +83,7 @@ com.oracle.database.jdbc ojdbc11 - {version.com.oracle.database.jdbc} + ${version.com.oracle.database.jdbc} diff --git a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoPostgreSqlContainer.java b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoPostgreSqlContainer.java index b0b798322a0..5c383d49a07 100644 --- a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoPostgreSqlContainer.java +++ b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoPostgreSqlContainer.java @@ -28,12 +28,13 @@ /** * PostgreSQL Container for Kogito examples. */ -public class KogitoPostgreSqlContainer extends PostgreSQLContainer implements TestResource { +public class KogitoPostgreSqlContainer extends PostgreSQLContainer implements TestResource { public static final String POSTGRESQL_CONNECTION_URI = "kogito.persistence.postgresql.connection.uri"; private static final Logger LOGGER = LoggerFactory.getLogger(KogitoPostgreSqlContainer.class); public KogitoPostgreSqlContainer() { + super("postgres:9.6.12"); withLogConsumer(getLogger()); withLogConsumer(new Slf4jLogConsumer(LOGGER)); withStartupTimeout(Constants.CONTAINER_START_TIMEOUT); From a43d518f347647c063794d3f0fa3dce1826338f4 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Tue, 21 Sep 2021 11:18:46 -0500 Subject: [PATCH 12/18] Update addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java Co-authored-by: Daniele Zonca --- .../java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java index c447a3413ff..d2fd558b5d8 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/JdbcProcessInstancesIT.java @@ -103,7 +103,7 @@ void testBasicFlow(DataSource dataSource) { } @Test - void testException(DataSource dataSource) { + void testException() { BpmnProcess process = configure(false); JDBCProcessInstances processInstances = (JDBCProcessInstances) process.instances(); assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> processInstances.findById(TEST_ID)); From 016426afa2a40b3ec51b6740f009498bf51888bf Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Tue, 21 Sep 2021 15:21:24 -0500 Subject: [PATCH 13/18] add README --- addons/common/persistence/jdbc/README.md | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 addons/common/persistence/jdbc/README.md diff --git a/addons/common/persistence/jdbc/README.md b/addons/common/persistence/jdbc/README.md new file mode 100644 index 00000000000..c03917e574d --- /dev/null +++ b/addons/common/persistence/jdbc/README.md @@ -0,0 +1,45 @@ +# Kogito JDBC Persistence Add-on + +The Kogito JDBC Persistence Add-on adds persistence capability to Kogito projects. See the [official documentation](https://docs.jboss.org/kogito/release/latest/html_single/#con-persistence_kogito-developing-process-services) to find out more. + +Currently tested for Postgres and Oracle. Other database will automatically use ANSI standard SQL. + +To enable JDBC persistence set the following value: +``` +kogito.persistence.type=jdbc +``` + +Example configuration for postgres +``` +# On Quarkus +quarkus.datasource.db-kind=postgresql +quarkus.datasource.username=postgres +quarkus.datasource.password=changeme +quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/kogito + +# On Spring Boot +spring.datasource.username=kogito-user +spring.datasource.password=kogito-pass +spring.datasource.url=jdbc:postgresql://localhost:5432/kogito +``` + +Example configuration for oracle +``` +# On Quarkus +quarkus.datasource.db-kind=oracle +quarkus.datasource.username=kogito-user +quarkus.datasource.password=kogito-user +quarkus.datasource.jdbc.url=jdbc:oracle:thin:@localhost:1521:kogito + +# On Spring Boot +spring.datasource.username=workflow +spring.datasource.password=workflow +spring.datasource.url=jdbc:oracle:thin:@localhost:1521:kogito +``` + +## Auto DLL creation +JDBC Persistence Add-on will attempt to automatically generate the necessary database objects if you enable the `autoDLL` property. +``` +kogito.persistence.auto.ddl=true +``` +This settings is defaulted to true. From 5d92483a431bb1cf05944cf5dde0b9b406e48171 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Wed, 22 Sep 2021 10:57:17 -0500 Subject: [PATCH 14/18] remove timezone requirements --- .../org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java index a18d8577f19..deab49f9c84 100644 --- a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java +++ b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java @@ -39,6 +39,7 @@ public KogitoOracleSqlContainer() { withLogConsumer(getLogger()); withLogConsumer(new Slf4jLogConsumer(LOGGER)); withStartupTimeout(Constants.CONTAINER_START_TIMEOUT); + System.setProperty("oracle.jdbc.timezoneAsRegion", "false"); } private Consumer getLogger() { From 28b070def60408081ae1bd71b0e6932cd36556cc Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Thu, 23 Sep 2021 10:27:05 -0500 Subject: [PATCH 15/18] sonar: check number of bytes read --- .../main/java/org/kie/kogito/persistence/jdbc/FileLoader.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java index b3a9fb04861..06f53408fd4 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java @@ -27,7 +27,9 @@ static List getQueryFromFile(final String dbType, final String scriptNam throw new IllegalStateException(String.format("Impossible to find %s", fileName)); } byte[] buffer = new byte[stream.available()]; - stream.read(buffer); + int count = 0; + while ((count = stream.read(buffer)) > 0) { + } String[] statements = new String(buffer).split(";"); return List.of(statements); } catch (Exception e) { From 903d6341159be4314bbc026adad9d054fe8aac05 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Thu, 30 Sep 2021 13:12:11 -0500 Subject: [PATCH 16/18] add sql creation script to ddl assembly --- addons/common/persistence/ddl/src/assembly/db-scripts.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/common/persistence/ddl/src/assembly/db-scripts.xml b/addons/common/persistence/ddl/src/assembly/db-scripts.xml index 4a81a2ac425..bf7bd43e107 100644 --- a/addons/common/persistence/ddl/src/assembly/db-scripts.xml +++ b/addons/common/persistence/ddl/src/assembly/db-scripts.xml @@ -18,7 +18,7 @@ ${path.to.persistence.modules}/jdbc/src/main/resources/sql jdbc - *_create.sql + create_table*.sql From 3981a6643b1f42c0e8e15fb2e4bec2579246f33f Mon Sep 17 00:00:00 2001 From: Daniele Zonca Date: Tue, 5 Oct 2021 11:38:10 +0200 Subject: [PATCH 17/18] Update db-scripts.xml --- .../ddl/src/assembly/db-scripts.xml | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/addons/common/persistence/ddl/src/assembly/db-scripts.xml b/addons/common/persistence/ddl/src/assembly/db-scripts.xml index bf7bd43e107..660f31eabd5 100644 --- a/addons/common/persistence/ddl/src/assembly/db-scripts.xml +++ b/addons/common/persistence/ddl/src/assembly/db-scripts.xml @@ -14,12 +14,17 @@ *_create.sql - - ${path.to.persistence.modules}/jdbc/src/main/resources/sql - jdbc - - create_table*.sql - - - \ No newline at end of file + + + ${path.to.persistence.modules}/jdbc/src/main/resources/sql/create_tables_Oracle.sql + oracle + runtime_create.sql + + + ${path.to.persistence.modules}/jdbc/src/main/resources/sql/create_tables_ansi.sql + jdbc + runtime_create.sql + + + From a2f0b0bb77c4ef97181f8862b8f552c1a2b47077 Mon Sep 17 00:00:00 2001 From: Kevin O'Neal Date: Tue, 5 Oct 2021 10:35:10 -0500 Subject: [PATCH 18/18] remove system property after test --- .../java/org/kie/kogito/persistence/jdbc/FileLoader.java | 5 +---- .../src/test/java/org/kie/persistence/jdbc/TestHelper.java | 3 +++ .../kie/kogito/testcontainers/KogitoOracleSqlContainer.java | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java index 06f53408fd4..e7a6a4b1633 100644 --- a/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java +++ b/addons/common/persistence/jdbc/src/main/java/org/kie/kogito/persistence/jdbc/FileLoader.java @@ -26,10 +26,7 @@ static List getQueryFromFile(final String dbType, final String scriptNam if (stream == null) { throw new IllegalStateException(String.format("Impossible to find %s", fileName)); } - byte[] buffer = new byte[stream.available()]; - int count = 0; - while ((count = stream.read(buffer)) > 0) { - } + byte[] buffer = stream.readAllBytes(); String[] statements = new String(buffer).split(";"); return List.of(statements); } catch (Exception e) { diff --git a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java index 42d1c4d4090..8ebf8ba9d38 100644 --- a/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java +++ b/addons/common/persistence/jdbc/src/test/java/org/kie/persistence/jdbc/TestHelper.java @@ -58,6 +58,7 @@ public static Stream datasources() { public static SecurityPolicy securityPolicy = SecurityPolicy.of(IdentityProviders.of("john")); public static final String TEST_ID = "02ac3854-46ee-42b7-8b63-5186c9889d96"; + private static final String ORACLE_TIMEZONE_PROPERTY = "oracle.jdbc.timezoneAsRegion"; @BeforeAll public static void startContainerAndPublicPortIsAvailable() { @@ -66,12 +67,14 @@ public static void startContainerAndPublicPortIsAvailable() { ORACLE_CONTAINER.start(); ORACLE_DATA_SOURCE = getOracleDataSource(ORACLE_CONTAINER); + System.setProperty(ORACLE_TIMEZONE_PROPERTY, "false"); } @AfterAll public static void close() { PG_CONTAINER.stop(); ORACLE_CONTAINER.stop(); + System.clearProperty(ORACLE_TIMEZONE_PROPERTY); } public static BpmnProcess createProcess(TestProcessInstancesFactory factory, ProcessConfig config, String fileName) { diff --git a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java index deab49f9c84..a18d8577f19 100644 --- a/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java +++ b/kogito-test-utils/src/main/java/org/kie/kogito/testcontainers/KogitoOracleSqlContainer.java @@ -39,7 +39,6 @@ public KogitoOracleSqlContainer() { withLogConsumer(getLogger()); withLogConsumer(new Slf4jLogConsumer(LOGGER)); withStartupTimeout(Constants.CONTAINER_START_TIMEOUT); - System.setProperty("oracle.jdbc.timezoneAsRegion", "false"); } private Consumer getLogger() {