diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyMaster.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyMaster.java index 10e9d06bd92..79370edc81a 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyMaster.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyMaster.java @@ -7,7 +7,9 @@ package com.ibm.fhir.database.utils.derby; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; @@ -28,17 +30,23 @@ * Set up an instance of Derby for use with unit tests */ public class DerbyMaster implements AutoCloseable { - + private static final Logger logger = Logger.getLogger(DerbyMaster.class.getName()); - + // The directory holding our derby databases private static final String DERBY_DIR = "derby/"; + // The derby properties file + private static final String DERBY_PROPERTIES = DERBY_DIR + "derby.properties"; + // The translator to help us out with Derby syntax private static final IDatabaseTranslator DERBY_TRANSLATOR = new DerbyTranslator(); - + // The name of the database we manage - private final String database; + private final String database; + + // Controls if we run derby in debugging mode which enables more logs. + private final boolean debugging = false; /** * Public constructor @@ -47,18 +55,28 @@ public class DerbyMaster implements AutoCloseable { */ public DerbyMaster(String database) { this.database = database; - - try { + + try (PrintWriter out = new PrintWriter(DERBY_PROPERTIES)){ Class.forName(DERBY_TRANSLATOR.getDriverClassName()); + // This speeds up sequence fetching by pre-creating 1000 instead of the default 100. + out.println("derby.language.sequence.preallocator=1000"); + if (debugging) { + out.println("derby.language.logQueryPlan=true"); + out.println("derby.language.logStatementText=true"); + out.println("derby.locks.deadlockTrace=true"); + out.println("derby.infolog.append=true"); + } } catch (ClassNotFoundException e) { throw new IllegalStateException(e); + } catch (FileNotFoundException e1) { + logger.warning("Failed to create derby.properties file!"); } - + dropDatabase(database); - + } - + /** * Drop the contents of the database on disk. Must reside in the derby/ directory * as a simple check against accidentally wiping the wrong files @@ -68,13 +86,13 @@ public static void dropDatabase(String database) { if (!database.startsWith(DERBY_DIR)) { throw new IllegalArgumentException("Derby databases must start with: " + DERBY_DIR); } - + try { File dir = new File(database); if (dir.exists()) { delete(dir); } - } + } catch (IOException e) { throw new IllegalStateException("Failed to delete derby DB: " + database, e); } @@ -92,7 +110,7 @@ private static void delete(File file) throws IOException { //directory is empty, then delete it if (file.list().length == 0) { file.delete(); - } + } else { // list all the directory contents String files[] = file.list(); @@ -110,7 +128,7 @@ private static void delete(File file) throws IOException { file.delete(); } } - } + } else { // if file exists, then delete it file.delete(); @@ -126,7 +144,7 @@ private static void delete(File file) throws IOException { public Connection createConnection() throws SQLException { logger.info("Opening connection to Derby database: " + database); Connection connection; - try { + try { // Make sure the Derby driver is loaded Properties properties = new Properties(); DerbyPropertyAdapter adapter = new DerbyPropertyAdapter(properties); @@ -136,7 +154,7 @@ public Connection createConnection() throws SQLException { } catch (SQLException x) { throw DERBY_TRANSLATOR.translate(x); - } + } return connection; } @@ -148,7 +166,7 @@ public Connection createConnection() throws SQLException { public IDatabaseTranslator getTranslator() { return DERBY_TRANSLATOR; } - + /** * Ask the schema to apply itself to our target (adapter pattern) * @param pdm @@ -167,7 +185,7 @@ public void runWithAdapter(java.util.function.Consumer fn) { try (Connection c = createConnection()) { try { JdbcTarget target = new JdbcTarget(c); - + if (logger.isLoggable(Level.FINE)) { // Decorate the target so that we print all the DDL before executing PrintTarget printer = new PrintTarget(target, logger.isLoggable(Level.FINE)); @@ -190,7 +208,7 @@ public void runWithAdapter(java.util.function.Consumer fn) { catch (SQLException x) { throw DERBY_TRANSLATOR.translate(x); } - + } @Override diff --git a/fhir-persistence-jdbc/src/test/java/com/ibm/fhir/persistence/jdbc/test/spec/R4JDBCExamplesTest.java b/fhir-persistence-jdbc/src/test/java/com/ibm/fhir/persistence/jdbc/test/spec/R4JDBCExamplesTest.java index 53ffab630e2..fb7bc7a8dff 100644 --- a/fhir-persistence-jdbc/src/test/java/com/ibm/fhir/persistence/jdbc/test/spec/R4JDBCExamplesTest.java +++ b/fhir-persistence-jdbc/src/test/java/com/ibm/fhir/persistence/jdbc/test/spec/R4JDBCExamplesTest.java @@ -6,12 +6,17 @@ package com.ibm.fhir.persistence.jdbc.test.spec; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; +import com.ibm.fhir.database.utils.api.ITransactionProvider; +import com.ibm.fhir.database.utils.pool.PoolConnectionProvider; +import com.ibm.fhir.database.utils.transaction.SimpleTransactionProvider; import com.ibm.fhir.model.spec.test.R4ExamplesDriver; import com.ibm.fhir.model.spec.test.R4ExamplesDriver.TestType; import com.ibm.fhir.model.test.TestUtil; @@ -19,7 +24,6 @@ import com.ibm.fhir.persistence.context.FHIRHistoryContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContextFactory; -import com.ibm.fhir.persistence.jdbc.impl.FHIRPersistenceJDBCImpl; import com.ibm.fhir.persistence.test.common.AbstractPersistenceTest; import com.ibm.fhir.schema.derby.DerbyFhirDatabase; @@ -44,10 +48,29 @@ public void bootstrapAndLoad() { @Test(groups = { "jdbc-seed" }) public void perform() throws Exception { - - R4JDBCExamplesProcessor processor = new R4JDBCExamplesProcessor(persistence, - () -> createPersistenceContext(), - () -> createHistoryPersistenceContext()); + // Use connection pool and transaction provider to make sure the resource operations + // of each resource are committed after the processing is finished, and because this + // testng test process the samples one by one, so set the connection pool size to 1. + PoolConnectionProvider connectionPool = new PoolConnectionProvider(database, 1); + ITransactionProvider transactionProvider = new SimpleTransactionProvider(connectionPool); + List operations = new ArrayList<>(); + operations.add(new CreateOperation()); + operations.add(new ReadOperation()); + operations.add(new UpdateOperation()); + operations.add(new UpdateOperation()); + operations.add(new ReadOperation()); + operations.add(new VReadOperation()); + operations.add(new HistoryOperation(3)); + operations.add(new DeleteOperation()); + operations.add(new DeleteOperation()); + operations.add(new HistoryOperation(4)); + R4JDBCExamplesProcessor processor = new R4JDBCExamplesProcessor( + operations, + this.properties, + connectionPool, + null, + null, + transactionProvider); // Overriding the JDBC ALL to Minimal. // Unless the profile tells us differently @@ -92,7 +115,8 @@ protected FHIRPersistenceContext createHistoryPersistenceContext() { @Override public FHIRPersistence getPersistenceImpl() throws Exception { - return new FHIRPersistenceJDBCImpl(this.properties, database); + // Return null to make sure the transaction operations in @BeforeMethod will be skipped + return null; } @Override diff --git a/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/common/AbstractPersistenceTest.java b/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/common/AbstractPersistenceTest.java index 37c5e4bf4d1..8e9227fd27f 100644 --- a/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/common/AbstractPersistenceTest.java +++ b/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/common/AbstractPersistenceTest.java @@ -86,14 +86,14 @@ public void setUp() throws Exception { @BeforeMethod(alwaysRun = true) public void startTrx() throws Exception{ - if (persistence.isTransactional()) { + if (persistence != null && persistence.isTransactional()) { persistence.getTransaction().begin(); } } @AfterMethod(alwaysRun = true) public void commitTrx() throws Exception{ - if (persistence.isTransactional()) { + if (persistence != null && persistence.isTransactional()) { persistence.getTransaction().commit(); } } @@ -117,11 +117,11 @@ protected List runQueryTest(Class resourceType, St protected List runQueryTest(Class resourceType, Map> queryParms) throws Exception { return runQueryTest(resourceType, queryParms, null); } - + protected List runQueryTest(Class resourceType, Map> queryParms, Integer maxPageSize) throws Exception { return runQueryTest(SearchUtil.parseQueryParameters(resourceType, queryParms), resourceType, queryParms, maxPageSize).getResource(); } - + protected MultiResourceResult runQueryTest(FHIRSearchContext searchContext, Class resourceType, Map> queryParms, Integer maxPageSize) throws Exception { // ensure that all the query parameters were processed into search parameters (needed because the server ignores invalid params by default) int expectedCount = 0;