From 9e6f15415439606887750610e009e823e1437502 Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Tue, 12 Nov 2024 16:18:32 -0600 Subject: [PATCH 1/8] test: Add unit tests for DatabaseContainerUtil --- .../DatabaseContainerPropertiesTest.java | 326 ++++++++++++++++++ .../database/container/authDataServer.xml | 25 ++ .../database/container/dataSourceServer.xml | 29 ++ .../database/container/libraryServer.xml | 19 + 4 files changed, 399 insertions(+) create mode 100644 dev/fattest.simplicity/test/componenttest/topology/database/container/DatabaseContainerPropertiesTest.java create mode 100644 dev/fattest.simplicity/test/componenttest/topology/database/container/authDataServer.xml create mode 100644 dev/fattest.simplicity/test/componenttest/topology/database/container/dataSourceServer.xml create mode 100644 dev/fattest.simplicity/test/componenttest/topology/database/container/libraryServer.xml diff --git a/dev/fattest.simplicity/test/componenttest/topology/database/container/DatabaseContainerPropertiesTest.java b/dev/fattest.simplicity/test/componenttest/topology/database/container/DatabaseContainerPropertiesTest.java new file mode 100644 index 00000000000..5f263c29b49 --- /dev/null +++ b/dev/fattest.simplicity/test/componenttest/topology/database/container/DatabaseContainerPropertiesTest.java @@ -0,0 +1,326 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package componenttest.topology.database.container; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.ibm.websphere.simplicity.config.AuthData; +import com.ibm.websphere.simplicity.config.DataSource; +import com.ibm.websphere.simplicity.config.DatabaseStore; +import com.ibm.websphere.simplicity.config.Fileset; +import com.ibm.websphere.simplicity.config.JavaPermission; +import com.ibm.websphere.simplicity.config.Library; +import com.ibm.websphere.simplicity.config.ServerConfiguration; +import com.ibm.websphere.simplicity.config.ServerConfigurationFactory; +import com.ibm.websphere.simplicity.config.dsprops.Properties; +import com.ibm.websphere.simplicity.config.dsprops.Properties_postgresql; + +import componenttest.topology.impl.LibertyServer; + +/** + * Tests the DatabaseContainerUtil (soon to be renamed DatabaseContainerProperties) class + */ +public class DatabaseContainerPropertiesTest { + + @Mock + LibertyServer server; + + @Mock + PostgreSQLContainer cont; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + + when(cont.getHost()).thenReturn("localhost"); + when(cont.getFirstMappedPort()).thenReturn(9999); + when(cont.getDatabaseName()).thenReturn("TEST"); + when(cont.getUsername()).thenReturn("TEST_USER"); + when(cont.getPassword()).thenReturn("TEST_PASS"); + } + + @Test + public void findAuthDataLocationsTest() throws Exception { + when(server.getServerConfiguration()).thenReturn(getServerConfigFor("authDataServer.xml")); + + DatabaseContainerUtil inst = DatabaseContainerUtil.build(server, cont); + + Set ads = (Set) getFindAuthDataLocations().invoke(inst, server.getServerConfiguration().getDataSources().getById("DefaultDataSource")); + + assertEquals(4, ads.size()); + + for(AuthData ad : ads) { + if(ad.getId() == null) { + assertEquals("myUser3", ad.getUser()); + assertEquals("myPass3", ad.getPassword()); + continue; + } + + if(ad.getId().equals("recoveryAuth")) { + assertEquals("myUser1", ad.getUser()); + assertEquals("myPass1", ad.getPassword()); + continue; + } + + if(ad.getId().equals("userAuth")) { + assertEquals("myUser2", ad.getUser()); + assertEquals("myPass2", ad.getPassword()); + continue; + } + + if(ad.getId().equals("contAuth")) { + assertEquals("myUser4", ad.getUser()); + assertEquals("myPass4", ad.getPassword()); + continue; + } + + fail("Found auth data that should not have been present in findAuthDataLocations: " + ad.toString()); + } + } + + @Test + public void withDriverReplacementTest() throws Exception { + assumeTrue(!System.getProperty("os.name").equalsIgnoreCase("OS/400")); + + when(server.getServerConfiguration()).thenReturn(getServerConfigFor("libraryServer.xml")); + + DatabaseContainerUtil inst = DatabaseContainerUtil.build(server, cont) + .withDriverReplacement(); + + ArgumentCaptor capturedConfig = ArgumentCaptor.forClass(ServerConfiguration.class); + doNothing().when(server).updateServerConfiguration(capturedConfig.capture()); + + inst.modify(); + + ServerConfiguration result = capturedConfig.getValue(); + + assertNotNull(result.getLibraries().getById("JDBCLibrary")); + + Library jdbcLib = result.getLibraries().getById("JDBCLibrary"); + assertEquals(1, jdbcLib.getFilesets().size()); + + Fileset jdbcFs = jdbcLib.getFilesets().get(0); + assertEquals("${shared.resource.dir}/jdbc", jdbcFs.getDir()); + assertEquals(DatabaseContainerType.Postgres.getDriverName(), jdbcFs.getIncludes()); + + assertEquals(1, result.getJavaPermissions().size()); + + JavaPermission permission = result.getJavaPermissions().get(0); + assertEquals("java.security.AllPermission", permission.getClassName()); + assertEquals("${shared.resource.dir}/jdbc/" + DatabaseContainerType.Postgres.getDriverName(), permission.getCodeBase()); + } + + @Test + public void withDriverVariableTest() throws Exception { + assumeTrue(!System.getProperty("os.name").equalsIgnoreCase("OS/400")); + + when(server.getServerConfiguration()).thenReturn(getServerConfigFor("libraryServer.xml")); + + DatabaseContainerUtil inst = DatabaseContainerUtil.build(server, cont) + .withDriverVariable(); + + ArgumentCaptor capturedConfig = ArgumentCaptor.forClass(ServerConfiguration.class); + doNothing().when(server).updateServerConfiguration(capturedConfig.capture()); + + inst.modify(); + + ServerConfiguration result = capturedConfig.getValue(); + + assertNotNull(result.getLibraries().getById("JDBCLibrary")); + + Library jdbcLib = result.getLibraries().getById("JDBCLibrary"); + assertEquals(1, jdbcLib.getFilesets().size()); + + Fileset jdbcFs = jdbcLib.getFilesets().get(0); + assertEquals("${shared.resource.dir}/jdbc", jdbcFs.getDir()); + assertEquals("${env.DB_DRIVER}", jdbcFs.getIncludes()); + + assertEquals(1, result.getJavaPermissions().size()); + + JavaPermission permission = result.getJavaPermissions().get(0); + assertEquals("java.security.AllPermission", permission.getClassName()); + assertEquals("${shared.resource.dir}/jdbc/${env.DB_DRIVER}", permission.getCodeBase()); + } + + @Test + public void withAuthVariablesTest() throws Exception { + assumeTrue(!System.getProperty("os.name").equalsIgnoreCase("OS/400")); + + when(server.getServerConfiguration()).thenReturn(getServerConfigFor("authDataServer.xml")); + + DatabaseContainerUtil inst = DatabaseContainerUtil.build(server, cont) + .withAuthVariables("myUserCustom", "myPassCustom"); + + ArgumentCaptor capturedConfig = ArgumentCaptor.forClass(ServerConfiguration.class); + doNothing().when(server).updateServerConfiguration(capturedConfig.capture()); + + inst.modify(); + + ServerConfiguration result = capturedConfig.getValue(); + + assertEquals(1, result.getDataSources().size()); + + DataSource defaultDataSource = result.getDataSources().get(0); + assertTrue(Boolean.valueOf(defaultDataSource.getFatModify())); + assertNotNull(defaultDataSource.getProperties()); + assertEquals(1, defaultDataSource.getProperties().size()); + + //Note: generic properties not properties.postgres because we did not specify .withDatabaseProperties() + assertEquals(1, defaultDataSource.getProperties().size()); + Properties dsProps = defaultDataSource.getProperties().get(0); + assertEquals("${env.DB_USER}", dsProps.getUser()); + assertEquals("${env.DB_PASS}", dsProps.getPassword()); + } + + @Test + public void withGenericPropertiesTest() throws Exception { + assumeTrue(!System.getProperty("os.name").equalsIgnoreCase("OS/400")); + + when(server.getServerConfiguration()).thenReturn(getServerConfigFor("dataSourceServer.xml")); + + DatabaseContainerUtil inst = DatabaseContainerUtil.build(server, cont) + .withGenericProperties(); + + ArgumentCaptor capturedConfig = ArgumentCaptor.forClass(ServerConfiguration.class); + doNothing().when(server).updateServerConfiguration(capturedConfig.capture()); + + inst.modify(); + + ServerConfiguration result = capturedConfig.getValue(); + + AuthData recoveryAuth = result.getAuthDataElements().getById("recoveryAuth"); + assertNotNull(recoveryAuth); + assertEquals("false", recoveryAuth.getFatModify()); + assertEquals("myUser1", recoveryAuth.getUser()); + assertEquals("myPass1", recoveryAuth.getPassword()); + + AuthData userAuth = result.getAuthDataElements().getById("userAuth"); + assertNotNull(userAuth); + assertEquals("true", userAuth.getFatModify()); + assertEquals(cont.getUsername(), userAuth.getUser()); + assertEquals(cont.getPassword(), userAuth.getPassword()); + + DataSource defaultDataSource = result.getDataSources().getById("DefaultDataSource"); + assertNotNull(defaultDataSource); + + assertEquals(1, defaultDataSource.getContainerAuthDatas().size()); + AuthData containerAuthData = defaultDataSource.getContainerAuthDatas().get(0); + assertNull(containerAuthData.getFatModify()); + assertEquals("myUser3", containerAuthData.getUser()); + assertEquals("myPass3", containerAuthData.getPassword()); + + //Note: generic properties because we specified .withGenericProperties(); + assertEquals(1, defaultDataSource.getProperties().size()); + Properties defaultDataSourceProperties = defaultDataSource.getProperties().get(0); + assertEquals(cont.getHost(), defaultDataSourceProperties.getServerName()); + assertEquals(cont.getFirstMappedPort().toString(), defaultDataSourceProperties.getPortNumber()); + assertEquals(cont.getDatabaseName(), defaultDataSourceProperties.getDatabaseName()); + assertNull("Expected null but was " + defaultDataSourceProperties.getUser(), defaultDataSourceProperties.getUser()); + assertNull("Expected null but was " + defaultDataSourceProperties.getPassword(), defaultDataSourceProperties.getPassword()); + + assertEquals(1, result.getDatabaseStores().size()); + DatabaseStore dbStore = result.getDatabaseStores().get(0); + + DataSource testDataSource = dbStore.getDataSources().getById("TestDataSource"); + assertNotNull(testDataSource); + + //Note: generic properties because we specified .withGenericProperties(); + assertEquals(1, testDataSource.getProperties().size()); + Properties testDataSourceProperties = testDataSource.getProperties().get(0); + assertEquals(cont.getHost(), testDataSourceProperties.getServerName()); + assertEquals(cont.getFirstMappedPort().toString(), testDataSourceProperties.getPortNumber()); + assertEquals(cont.getDatabaseName(), testDataSourceProperties.getDatabaseName()); + assertEquals(cont.getUsername(), testDataSourceProperties.getUser()); + assertEquals(cont.getPassword(), testDataSourceProperties.getPassword()); + } + + @Test + public void withDatabaseProperties() throws Exception { + when(server.getServerConfiguration()).thenReturn(getServerConfigFor("dataSourceServer.xml")); + + DatabaseContainerUtil inst = DatabaseContainerUtil.build(server, cont) + .withDatabaseProperties(); + + ArgumentCaptor capturedConfig = ArgumentCaptor.forClass(ServerConfiguration.class); + doNothing().when(server).updateServerConfiguration(capturedConfig.capture()); + + inst.modify(); + + ServerConfiguration result = capturedConfig.getValue(); + + DataSource defaultDataSource = result.getDataSources().getById("DefaultDataSource"); + assertNotNull(defaultDataSource); + + assertEquals(1, defaultDataSource.getContainerAuthDatas().size()); + AuthData containerAuthData = defaultDataSource.getContainerAuthDatas().get(0); + assertNull(containerAuthData.getFatModify()); + assertEquals("myUser3", containerAuthData.getUser()); + assertEquals("myPass3", containerAuthData.getPassword()); + + //Note: specific properties because we specified .withDatabaseProperties() + assertEquals(1, defaultDataSource.getProperties_postgresql().size()); + Properties_postgresql defaultDataSourceProperties = defaultDataSource.getProperties_postgresql().get(0); + assertEquals(cont.getHost(), defaultDataSourceProperties.getServerName()); + assertEquals(cont.getFirstMappedPort().toString(), defaultDataSourceProperties.getPortNumber()); + assertEquals(cont.getDatabaseName(), defaultDataSourceProperties.getDatabaseName()); + assertNull("Expected null but was " + defaultDataSourceProperties.getUser(), defaultDataSourceProperties.getUser()); + assertNull("Expected null but was " + defaultDataSourceProperties.getPassword(), defaultDataSourceProperties.getPassword()); + + assertEquals(1, result.getDatabaseStores().size()); + DatabaseStore dbStore = result.getDatabaseStores().get(0); + + DataSource testDataSource = dbStore.getDataSources().getById("TestDataSource"); + assertNotNull(testDataSource); + + //Note: specific properties because we specified .withDatabaseProperties() + assertEquals(1, testDataSource.getProperties_postgresql().size()); + Properties_postgresql testDataSourceProperties = testDataSource.getProperties_postgresql().get(0); + assertEquals(cont.getHost(), testDataSourceProperties.getServerName()); + assertEquals(cont.getFirstMappedPort().toString(), testDataSourceProperties.getPortNumber()); + assertEquals(cont.getDatabaseName(), testDataSourceProperties.getDatabaseName()); + assertEquals(cont.getUsername(), testDataSourceProperties.getUser()); + assertEquals(cont.getPassword(), testDataSourceProperties.getPassword()); + } + + // Helper methods to use test server.xml files + + private static ServerConfiguration getServerConfigFor(String fileName) throws Exception { + return ServerConfigurationFactory.getInstance().unmarshal(getServerStream(fileName)); + } + + private static InputStream getServerStream(String fileName) { + return DatabaseContainerPropertiesTest.class.getResourceAsStream(fileName); + } + + // Helper methods to get private methods + + private static Method getFindAuthDataLocations() throws Exception { + Method m = DatabaseContainerUtil.class.getDeclaredMethod("findAuthDataLocations", DataSource.class); + m.setAccessible(true); + return m; + } +} diff --git a/dev/fattest.simplicity/test/componenttest/topology/database/container/authDataServer.xml b/dev/fattest.simplicity/test/componenttest/topology/database/container/authDataServer.xml new file mode 100644 index 00000000000..41ecb989a8d --- /dev/null +++ b/dev/fattest.simplicity/test/componenttest/topology/database/container/authDataServer.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/dev/fattest.simplicity/test/componenttest/topology/database/container/dataSourceServer.xml b/dev/fattest.simplicity/test/componenttest/topology/database/container/dataSourceServer.xml new file mode 100644 index 00000000000..8c89d8fab09 --- /dev/null +++ b/dev/fattest.simplicity/test/componenttest/topology/database/container/dataSourceServer.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dev/fattest.simplicity/test/componenttest/topology/database/container/libraryServer.xml b/dev/fattest.simplicity/test/componenttest/topology/database/container/libraryServer.xml new file mode 100644 index 00000000000..d714b05258d --- /dev/null +++ b/dev/fattest.simplicity/test/componenttest/topology/database/container/libraryServer.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file From 8e3002dd6f323e08c8277a4d09bdb80419b3a08a Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Tue, 12 Nov 2024 16:19:11 -0600 Subject: [PATCH 2/8] refactor: make DatabaseContainerUtil a build class Make it easier in the future to add methods and customization. Allow for an alternative set of authentication data that does not change between databases using withAuthVariables --- .../container/DatabaseContainerType.java | 2 +- .../container/DatabaseContainerUtil.java | 704 +++++++++--------- 2 files changed, 367 insertions(+), 339 deletions(-) diff --git a/dev/fattest.simplicity/src/componenttest/topology/database/container/DatabaseContainerType.java b/dev/fattest.simplicity/src/componenttest/topology/database/container/DatabaseContainerType.java index 44ffdd215e1..469d7b4e91f 100644 --- a/dev/fattest.simplicity/src/componenttest/topology/database/container/DatabaseContainerType.java +++ b/dev/fattest.simplicity/src/componenttest/topology/database/container/DatabaseContainerType.java @@ -177,7 +177,7 @@ public T cast(Object instance) { */ public static DatabaseContainerType valueOf(JdbcDatabaseContainer cont) { for (DatabaseContainerType elem : values()) - if (elem.getContainerClass() == cont.getClass()) + if (elem.getContainerClass().isInstance(cont)) return elem; throw new IllegalArgumentException("Unrecognized JdbcDatabaseContainer class: " + cont.getClass().getCanonicalName()); } diff --git a/dev/fattest.simplicity/src/componenttest/topology/database/container/DatabaseContainerUtil.java b/dev/fattest.simplicity/src/componenttest/topology/database/container/DatabaseContainerUtil.java index a16c349bd9c..f99f7e28dc6 100644 --- a/dev/fattest.simplicity/src/componenttest/topology/database/container/DatabaseContainerUtil.java +++ b/dev/fattest.simplicity/src/componenttest/topology/database/container/DatabaseContainerUtil.java @@ -15,12 +15,16 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; import org.testcontainers.containers.JdbcDatabaseContainer; import com.ibm.websphere.simplicity.config.AuthData; -import com.ibm.websphere.simplicity.config.ConfigElementList; +import com.ibm.websphere.simplicity.config.ConfigElement; import com.ibm.websphere.simplicity.config.DataSource; import com.ibm.websphere.simplicity.config.DataSourceProperties; import com.ibm.websphere.simplicity.config.DatabaseStore; @@ -33,419 +37,443 @@ import componenttest.topology.impl.LibertyServer; +/** + *
+ * When using database rotation the server configuration needs to be updated
+ * for the database which is currenlty under test. This class updates server
+ * configuration by following this routine:
+ *
+ * 1. Retrieves database specific properties from the provided JdbcDatabaseContainer, such as;
+ * databaseName, server, port, username, password, etc.
+ *
+ * 2. Using the ServerConfiguration API.
+ * Retrieves all {@code }, {@code }, and {@code} elements
+ * and modifies those that have the fat.modify=true attribute set. 
+ * + * 3. Replace the dataSource {@code } + * with the generic {@code } or specific {@code } + * element for the provided JdbcDatabaseContainer.
+ * + *
+ * + * @see com.ibm.websphere.simplicity.config.ServerConfiguration + */ +//TODO Change class to DatabaseContainerProperties public final class DatabaseContainerUtil { //Logging Constants private static final Class c = DatabaseContainerUtil.class; - private static String DRIVER_REPLACEMENT = "${env.DB_DRIVER}"; + //Driver replacement key + private static String DRIVER_KEY = "DB_DRIVER"; + private static String USER_KEY = "DB_USER"; + private static String PASS_KEY = "DB_PASS"; + + private static final String toReplacementString(String key) { + return "${env." + key + "}"; + } + + //Required fields + private final LibertyServer server; + private final ServerConfiguration serverClone; + private final JdbcDatabaseContainer databaseCont; + private final DatabaseContainerType databaseType; - private enum AuthLocation { - inProperties, - inAuthDataRef, - inEmbeddedAuthData, - none; + //Optional fields + private boolean useGeneric = true; - public String id; + //Required updates + private final Set datasources; + private final Set authDatas; - public AuthLocation withID(String id) { - this.id = id; - return this; + //Optional updates + private Map libraries = Collections.emptyMap(); + private Set permissions = Collections.emptySet(); + + ///// Constructor ///// + private DatabaseContainerUtil(LibertyServer serv, JdbcDatabaseContainer cont) throws Exception { + this.server = Objects.requireNonNull(serv); + this.serverClone = server.getServerConfiguration().clone(); + + this.databaseCont = Objects.requireNonNull(cont); + this.databaseType = DatabaseContainerType.valueOf(databaseCont); + + boolean isDerby = DatabaseContainerType.valueOf(databaseCont) == DatabaseContainerType.Derby || + DatabaseContainerType.valueOf(databaseCont) == DatabaseContainerType.DerbyClient; + + if (isDerby) { + this.datasources = Collections.emptySet(); + this.authDatas = Collections.emptySet(); + return; } - } - private DatabaseContainerUtil() { - //No objects should be created from this class + //Get a list of datasources that need to be updated + Set dsSet = new HashSet<>(); + + //Get general dataSources + dsSet.addAll(serverClone.getDataSources()); + + //Get datasources that are nested under databasestores + for (DatabaseStore dbs : serverClone.getDatabaseStores()) { + dsSet.addAll(dbs.getDataSources()); + } + + this.datasources = dsSet.stream() + .filter(ds -> ds.getFatModify() != null) + .filter(ds -> ds.getFatModify().equalsIgnoreCase("true")) + .collect(Collectors.toSet()); + + //Get a set of distinct authDatas that could be updated + this.authDatas = this.datasources.stream() + .flatMap(ds -> findAuthDataLocations(ds).stream()) + .distinct() + .collect(Collectors.toSet()); + + //TODO what about authData elements inside a element? } - /** - * performs the same property substitution as {@link DatabaseContainerUtil#setupDataSourceDatabaseProperties(LibertyServer, JdbcDatabaseContainer)} - * but ensures we find and swap ${DB_DRIVER} from all filesets because the JVM will return null for env vars before checkpoint. - */ - public static void setupDataSourcePropertiesForCheckpoint(LibertyServer serv, JdbcDatabaseContainer cont) throws CloneNotSupportedException, Exception { - boolean isDerby = DatabaseContainerType.valueOf(cont) == DatabaseContainerType.Derby || DatabaseContainerType.valueOf(cont) == DatabaseContainerType.DerbyClient; - - //Get server config - ServerConfiguration cloneConfig = serv.getServerConfiguration().clone(); - //Get datasources to be changed or skip if derby - Map datasources = isDerby ? Collections.emptyMap() : getDataSources(cloneConfig); - //Get authdatas to be changed or skip if derby - Map authDatas = isDerby ? Collections.emptyMap() : getAuthDatas(cloneConfig); - //Get libraries to be changed - Map libraries = getLibraries(cloneConfig); - //Modify those datasources - modifyDataSourcePropsForDatabase(datasources, authDatas, libraries, cloneConfig, serv, cont); - } + ///// Builder ///// /** - * Performs the same property substitution as {@link DatabaseContainerUtil#setupDataSourceProperties(LibertyServer, JdbcDatabaseContainer)} - * but ensures that we use properties.{database} instead of generic properties. + * @param serv LibertyServer server instance being used for this FAT suite. + * @param cont JdbcDatabaseContainer instance being used for database connectivity. + * @return instance of DatabaseContainerUtil */ - public static void setupDataSourceDatabaseProperties(LibertyServer serv, JdbcDatabaseContainer cont) throws CloneNotSupportedException, Exception { - //Skip for Derby and DerbyClient - if (DatabaseContainerType.valueOf(cont) == DatabaseContainerType.Derby || - DatabaseContainerType.valueOf(cont) == DatabaseContainerType.DerbyClient) - return; //Derby used by default no need to change DS properties - - //Get server config - ServerConfiguration cloneConfig = serv.getServerConfiguration().clone(); - //Get datasources to be changed - Map datasources = getDataSources(cloneConfig); - //Get authdatas to be changed - Map authDatas = getAuthDatas(cloneConfig); - //Provide empty list of libraries - Map libraries = Collections.emptyMap(); - //Modify those datasources - modifyDataSourcePropsForDatabase(datasources, authDatas, libraries, cloneConfig, serv, cont); + public static DatabaseContainerUtil build(LibertyServer server, JdbcDatabaseContainer cont) { + try { + return new DatabaseContainerUtil(server, cont); + } catch (Exception e) { + throw new RuntimeException("Failure while building database container util", e); + } } + ///// Configuration ///// + /** - * For use when attempting to use database rotation.
+ * Performs substitution of the library element with the name of the driver for the database used at runtime. + * This is helpful during checkpoint tests since the JVM will return null for environment variables before checkpoint. * - * Retrieves database specific properties from the provided JdbcDatabaseContainer, such as; - * username, password, etc.
+ * Example (${env.DB_DRIVER} will be replaced): * - * Using the ServerConfiguration API. Retrieves all <dataSource> elements and modifies - * those that have the fat.modify=true attribute set.
+ *
+     * 
+     *   <library id="JDBCLibrary" >
+     *     <fileset dir="${shared.resource.dir}/jdbc" includes="${env.DB_DRIVER}" />
+     *   </library>
+     * 
+     * 
* - * This will replace the datasource <derby.*.properties... > with the generic properties - * for the provided JdbcDatabaseContainer.
+ * TODO consider requiring fat.modify = true for this replacement + * @return this + */ + public DatabaseContainerUtil withDriverReplacement() { + this.libraries = new HashMap<>(); + + //Get filesets + for (Library lib : serverClone.getLibraries()) + for (Fileset fs : lib.getFilesets()) + if (fs.getIncludes().equals(toReplacementString(DRIVER_KEY))) + libraries.put(getElementId(lib), fs); //Reference library id here since it will be more recognizable + + this.permissions = serverClone.getJavaPermissions().stream() + .filter(p -> p.getCodeBase().contains(toReplacementString(DRIVER_KEY))) + .collect(Collectors.toSet()); + + return this; + } + + /** + * Adds the environment variable `DB_DRIVER` to the server to avoid having to modify the library element. * - * @see com.ibm.websphere.simplicity.config.ServerConfiguration + * Example: * - * @param serv - LibertyServer server instance being used for this FAT suite. - * @param cont - JdbcDatabaseContainer instance being used for database connectivity. + *
+     * 
+     *   <library id="JDBCLibrary">
+     *     <fileset dir="${shared.resource.dir}/jdbc" includes="${env.DB_DRIVER}" />
+     *   </library>
+     * 
+     * 
* - * @throws Exception + * @return this */ - public static void setupDataSourceProperties(LibertyServer serv, JdbcDatabaseContainer cont) throws Exception { - //Skip for Derby and DerbyClient - if (DatabaseContainerType.valueOf(cont) == DatabaseContainerType.Derby || - DatabaseContainerType.valueOf(cont) == DatabaseContainerType.DerbyClient) - return; //Derby used by default no need to change DS properties - - //If a test suite legitimately wants to call this method outside of the Database Rotation SOE - //Then we need to fail them on the IBMi SOE to avoid generic errors that arise when trying to infer datasource types. - if (System.getProperty("os.name").equalsIgnoreCase("OS/400")) { - throw new IllegalStateException("Attempting to modify the DataSource server configuration with a generic element on an IBMi server. " - + " IBMi ships with a JDK that has a DB2 driver globally available which means we cannot infer the datasource type. " - + " Switch to use the setupDataSourceDatabaseProperties method."); - } - - //Get server config - ServerConfiguration cloneConfig = serv.getServerConfiguration().clone(); - //Get datasources to be changed - Map datasources = getDataSources(cloneConfig); - //Get authdatas to be changed - Map authDatas = getAuthDatas(cloneConfig); - //Provide empty list of libraries - Map libraries = Collections.emptyMap(); - //Modify those datasources - modifyDataSourcePropsGeneric(datasources, authDatas, libraries, cloneConfig, serv, cont); + public DatabaseContainerUtil withDriverVariable() { + this.server.addEnvVar("DB_DRIVER", databaseType.getDriverName()); + return this; } - /* - * Helper method to get a list of datasources that need to be updated + /** + * Adds the additional environment variables `DB_USER` and `DB_PASS` to the server to allow for + * customized user/password variables that are not the system/admin authentication data. + * + * @param user a database user + * @param pass a database user's password + * + * @return this */ - private static Map getDataSources(ServerConfiguration cloneConfig) { - //Get a list of datasources that need to be updated - Map datasources = new HashMapWithNullKeys<>(); - - //Get general datasources - for (DataSource ds : cloneConfig.getDataSources()) - if (ds.getFatModify() != null && ds.getFatModify().equals("true")) - datasources.put(ds.getId(), ds); - - //Get datasources that are nested under databasestores - for (DatabaseStore dbs : cloneConfig.getDatabaseStores()) - for (DataSource ds : dbs.getDataSources()) - if (ds.getFatModify() != null && ds.getFatModify().equals("true")) - datasources.put(ds.getId(), ds); - - return datasources; + public DatabaseContainerUtil withAuthVariables(String user, String pass) { + this.server.addEnvVar("DB_USER", user); + this.server.addEnvVar("DB_PASS", pass); + return this; } - /* - * Helper method to get a list of AuthData elements that need to be updated + /** + * Performs substitution of the dataSource element {@code } + * with a set of generic properties {@code } + * + * NOTE: this is the default behavior + * + * @return this */ - private static Map getAuthDatas(ServerConfiguration cloneConfig) { - //Get a list of authDatas that need to be updated - Map authDatas = new HashMapWithNullKeys<>(); - - //Get general authDatas - for (AuthData ad : cloneConfig.getAuthDataElements()) - if (ad.getFatModify() != null && ad.getFatModify().equals("true")) - authDatas.put(ad.getId(), ad); - - //Get authDatas that are nested under datasources - for (DataSource ds : cloneConfig.getDataSources()) { - ConfigElementList ads = new ConfigElementList<>(); - ads.addAll(ds.getContainerAuthDatas()); - ads.addAll(ds.getRecoveryAuthDatas()); - for (AuthData ad : ads) - if (ad.getFatModify() != null && ad.getFatModify().equals("true")) - authDatas.put(ad.getId(), ad); - } - - return authDatas; + public DatabaseContainerUtil withGenericProperties() { + useGeneric = true; + return this; } - /* - * Helper method to get a list of Libraries that need to be modified + /** + * Performs substitution of the dataSource element {@code } + * with a set of specific properties for the database under test {@code } + * + * @return this */ - private static Map getLibraries(ServerConfiguration cloneConfig) { - //Get a list of authDatas that need to be updated - Map filesets = new HashMapWithNullKeys<>(); - - //Get filesets - for (Library lib : cloneConfig.getLibraries()) - for (Fileset fs : lib.getFilesets()) - if (fs.getIncludes().equals(DRIVER_REPLACEMENT)) - filesets.put(lib.getId(), fs); //Reference library id here since it will be more recognizable - - return filesets; + public DatabaseContainerUtil withDatabaseProperties() { + useGeneric = false; + return this; } - /* - * Creates generic properties for each database - */ - private static void modifyDataSourcePropsGeneric(Map datasources, Map authDatas, Map libraries, - ServerConfiguration cloneConfig, LibertyServer serv, - JdbcDatabaseContainer cont) throws Exception { - //Get database type - DatabaseContainerType type = DatabaseContainerType.valueOf(cont); + ///// Termination ///// + public void modify() throws Exception { + //If a test suite legitimately wants to call this method outside of the Database Rotation SOE + //Then we need to fail them on the IBMi SOE to avoid generic errors that arise when trying to infer datasource types. + if (useGeneric && System.getProperty("os.name").equalsIgnoreCase("OS/400")) { + throw new IllegalStateException("Attempting to modify the DataSource server configuration with a generic element on an IBMi server. " + + " IBMi ships with a JDK that has a DB2 driver globally available which means we cannot infer the datasource type. " + + " Switch to use the setupDataSourceDatabaseProperties method."); + } + // modify datasources if (!datasources.isEmpty()) { - //Create general properties - DataSourceProperties props = new Properties(); - props.setUser(cont.getUsername()); - props.setPassword(cont.getPassword()); - props.setServerName(cont.getHost()); - props.setPortNumber(Integer.toString(cont.getFirstMappedPort())); + + //Create generic or specific properties + DataSourceProperties commonProps = useGeneric ? new Properties() : databaseType.getDataSourceProps(); + + //Common configuration + commonProps.setServerName(databaseCont.getHost()); + commonProps.setPortNumber(Integer.toString(databaseCont.getFirstMappedPort())); try { - props.setDatabaseName(cont.getDatabaseName()); + commonProps.setDatabaseName(databaseCont.getDatabaseName()); } catch (UnsupportedOperationException e) { - if (type.equals(DatabaseContainerType.SQLServer)) { - props.setDatabaseName("TEST"); + if (databaseType.equals(DatabaseContainerType.SQLServer)) { + commonProps.setDatabaseName("TEST"); } } - //TODO this should not be required even when using general datasource properties - // investigating here: https://github.com/OpenLiberty/open-liberty/issues/10066 - if (type.equals(DatabaseContainerType.DB2)) { - props.setExtraAttribute("driverType", "4"); - } + //Specific configuration + if (useGeneric) { + //TODO this should not be required even when using general datasource properties + // investigating here: https://github.com/OpenLiberty/open-liberty/issues/10066 + if (databaseType.equals(DatabaseContainerType.DB2)) { + commonProps.setExtraAttribute("driverType", "4"); + } - if (type.equals(DatabaseContainerType.SQLServer)) { - props.setExtraAttribute("selectMethod", "cursor"); - } + if (databaseType.equals(DatabaseContainerType.SQLServer)) { + commonProps.setExtraAttribute("selectMethod", "cursor"); + } - if (type.equals(DatabaseContainerType.Oracle)) { - Class clazz = type.getContainerClass(); - Method getSid = clazz.getMethod("getSid"); - props.setDatabaseName((String) getSid.invoke(cont)); - props.setExtraAttribute("driverType", "thin"); + if (databaseType.equals(DatabaseContainerType.Oracle)) { + Class clazz = databaseType.getContainerClass(); + Method getSid = clazz.getMethod("getSid"); + commonProps.setDatabaseName((String) getSid.invoke(databaseCont)); + commonProps.setExtraAttribute("driverType", "thin"); + } + } else { + if (databaseType.equals(DatabaseContainerType.Oracle)) { + Class clazz = databaseType.getContainerClass(); + Method getSid = clazz.getMethod("getSid"); + commonProps.setDatabaseName((String) getSid.invoke(databaseCont)); + } } - // Same properties just without auth data when using containerAuthDataRef, or containerAuthData. - DataSourceProperties noAuthProps = (DataSourceProperties) props.clone(); - noAuthProps.setUser(null); - noAuthProps.setPassword(null); - - for (Map.Entry entry : datasources.entrySet()) { - Log.info(c, "setupDataSourceProperties", "FOUND: DataSource to be enlisted in database rotation. ID: " + entry.getKey()); - - AuthLocation authLoc = whereIsAuthData(entry.getValue()); - switch (authLoc) { - case inAuthDataRef: - assertAuthDataWillBeModified(entry.getKey(), authLoc.id, authDatas); - entry.getValue().replaceDatasourceProperties(noAuthProps); - break; - case inEmbeddedAuthData: - assertAuthDataWillBeModified(entry.getKey(), authLoc.id, authDatas); - entry.getValue().replaceDatasourceProperties(noAuthProps); - break; - case inProperties: - entry.getValue().replaceDatasourceProperties(props); - break; - case none: - Log.warning(c, - "The datasource (" + entry.getKey() - + ") did not have username/password set in properties element, ContainerAuthData element, or ContainerAuthDataRef attribute. " - + "We will assume you have configured a DeploymentDescriptor that DOES reference a AuthData element that has been modified."); - entry.getValue().replaceDatasourceProperties(noAuthProps); - break; - default: - //Replace default properties - entry.getValue().replaceDatasourceProperties(props); - break; + //Update DataSources + for (DataSource ds : datasources) { + Log.info(c, "modify", "FOUND: DataSource to be enlisted in database rotation. ID: " + getElementId(ds)); + if(ds.getDataSourceProperties().size() != 1) { + throw new RuntimeException("Expected exactly one set of DataSoure properties for DataSource: " + getElementId(ds)); } + + //Make a clone of common props and determine username/password + DataSourceProperties clone = (DataSourceProperties) commonProps.clone(); + + for(DataSourceProperties originalProps : ds.getDataSourceProperties()) { + if(canUpdate(originalProps)) { + clone.setUser(databaseCont.getUsername()); + clone.setPassword(databaseCont.getPassword()); + } else { + clone.setUser(originalProps.getUser()); + clone.setPassword(originalProps.getPassword()); + } + } + + //Replace dataSource properties + ds.replaceDatasourceProperties(clone); } } - for (Map.Entry entry : authDatas.entrySet()) { - Log.info(c, "setupDataSourceProperties", "FOUND: AuthData to be enlisted in database rotation. ID: " + entry.getKey()); - //Replace derby auth data - entry.getValue().setUser(cont.getUsername()); - entry.getValue().setPassword(cont.getPassword()); + // Modify authDatas + for (AuthData ad : authDatas) { + if(!canUpdate(ad)) { + Log.info(c, "modify", "SKIP: AuthData cannot be enlisted in database rotation. ID: " + getElementId(ad)); + continue; + } + + Log.info(c, "modify", "FOUND: AuthData to be enlisted in database rotation. ID: " + getElementId(ad)); + + ad.setUser(databaseCont.getUsername()); + ad.setPassword(databaseCont.getPassword()); } + // Modify libraries for (Map.Entry entry : libraries.entrySet()) { - Log.info(c, "setupDataSourceProperties", "FOUND: Library to be enlisted in database rotation. ID: " + entry.getKey()); + Log.info(c, "modify", "FOUND: Library to be enlisted in database rotation. ID: " + entry.getKey()); + //Replace includes with driver name - entry.getValue().setIncludes(type.getDriverName()); - //Add all permissions for driver - JavaPermission driverPermission = new JavaPermission(); - driverPermission.setCodeBase("${shared.resource.dir}/jdbc/" + type.getDriverName()); - driverPermission.setClassName("java.security.AllPermission"); - cloneConfig.getJavaPermissions().add(driverPermission); + entry.getValue().setIncludes(databaseType.getDriverName()); + } + + // Modify permissions + for (JavaPermission permission : permissions) { + Log.info(c, "modify", "FOUND: Permission to be enlisted in database rotation. ID: " + getElementId(permission)); + + String codeBase = permission.getCodeBase(); + permission.setCodeBase(codeBase.replace(toReplacementString(DRIVER_KEY), databaseType.getDriverName())); } //Update config - serv.updateServerConfiguration(cloneConfig); + server.updateServerConfiguration(serverClone); } - /* - * Creates properties for specific database - */ - private static void modifyDataSourcePropsForDatabase(Map datasources, Map authDatas, Map libraries, - ServerConfiguration cloneConfig, LibertyServer serv, - JdbcDatabaseContainer cont) throws Exception { - //Get database type - DatabaseContainerType type = DatabaseContainerType.valueOf(cont); - - if (!datasources.isEmpty()) { - //Create properties based on type - DataSourceProperties props = type.getDataSourceProps(); - props.setUser(cont.getUsername()); - props.setPassword(cont.getPassword()); - props.setServerName(cont.getHost()); - props.setPortNumber(Integer.toString(cont.getFirstMappedPort())); - try { - props.setDatabaseName(cont.getDatabaseName()); - } catch (UnsupportedOperationException e) { - if (type.equals(DatabaseContainerType.SQLServer)) { - props.setDatabaseName("TEST"); - } - } - - if (type.equals(DatabaseContainerType.Oracle)) { - Class clazz = type.getContainerClass(); - Method getSid = clazz.getMethod("getSid"); - props.setDatabaseName((String) getSid.invoke(cont)); - } - - // Same properties just without auth data when using containerAuthDataRef, or containerAuthData. - DataSourceProperties noAuthProps = (DataSourceProperties) props.clone(); - noAuthProps.setUser(null); - noAuthProps.setPassword(null); - - for (Map.Entry entry : datasources.entrySet()) { - Log.info(c, "setupDataSourceProperties", "FOUND: DataSource to be enlisted in database rotation. ID: " + entry.getKey()); - - AuthLocation authLoc = whereIsAuthData(entry.getValue()); - switch (authLoc) { - case inAuthDataRef: - assertAuthDataWillBeModified(entry.getKey(), authLoc.id, authDatas); - entry.getValue().replaceDatasourceProperties(noAuthProps); - break; - case inEmbeddedAuthData: - assertAuthDataWillBeModified(entry.getKey(), authLoc.id, authDatas); - entry.getValue().replaceDatasourceProperties(noAuthProps); - break; - case inProperties: - entry.getValue().replaceDatasourceProperties(props); - break; - case none: - Log.warning(c, - "The datasource (" + entry.getKey() - + ") did not have username/password set in properties element, ContainerAuthData element, or ContainerAuthDataRef attribute. " - + "We will assume you have configured a DeploymentDescriptor that DOES reference a AuthData element that has been modified."); - entry.getValue().replaceDatasourceProperties(noAuthProps); - break; - default: - //Replace default properties - entry.getValue().replaceDatasourceProperties(props); - break; - - } - } - } - - for (Map.Entry entry : authDatas.entrySet()) { - Log.info(c, "setupDataSourceProperties", "FOUND: AuthData to be enlisted in database rotation. ID: " + entry.getKey()); - //Replace derby auth data - entry.getValue().setUser(cont.getUsername()); - entry.getValue().setPassword(cont.getPassword()); - } + ///// Deprecated static methods //// - for (Map.Entry entry : libraries.entrySet()) { - Log.info(c, "setupDataSourceProperties", "FOUND: Library to be enlisted in database rotation. ID: " + entry.getKey()); - //Replace includes with driver name - entry.getValue().setIncludes(type.getDriverName()); - //Add all permissions for driver - JavaPermission driverPermission = new JavaPermission(); - driverPermission.setCodeBase("${shared.resource.dir}/jdbc/" + type.getDriverName()); - driverPermission.setClassName("java.security.AllPermission"); - cloneConfig.getJavaPermissions().add(driverPermission); - } + /** + * Instead use: + * + * DatabaseContainerUtil.build(server, cont).withDriverReplacement().withDatabaseProperties().modify(); + * + */ + @Deprecated //TODO remove once Websphere Liberty repository is updated + public static void setupDataSourcePropertiesForCheckpoint(LibertyServer serv, JdbcDatabaseContainer cont) throws CloneNotSupportedException, Exception { + DatabaseContainerUtil.build(serv, cont).withDriverReplacement().withDatabaseProperties().modify(); + } - //Update config - serv.updateServerConfiguration(cloneConfig); + /** + * Instead use: + * + * DatabaseContainerUtil.build(server, cont).withDatabaseProperties().modify(); + * + */ + @Deprecated //TODO remove once Websphere Liberty repository is updated + public static void setupDataSourceDatabaseProperties(LibertyServer serv, JdbcDatabaseContainer cont) throws CloneNotSupportedException, Exception { + DatabaseContainerUtil.build(serv, cont).withDatabaseProperties().modify(); } /** - * @param id - * @param authDatas + * Instead use: + * + * DatabaseContainerUtil.build(server, cont).modify(); + * */ - private static void assertAuthDataWillBeModified(String dsID, String authID, Map authDatas) { - if (!authDatas.containsKey(authID)) { - throw new RuntimeException("The datasource (" + dsID + ") references an AuthData element (" + authID + ") but that element is not modifiable."); - } + @Deprecated + public static void setupDataSourceProperties(LibertyServer serv, JdbcDatabaseContainer cont) throws Exception { + DatabaseContainerUtil.build(serv, cont).modify(); } + ///// Helper methods ///// /** - * Determine if a datasource has a username/password configured on a properties element. - * - * @param datasource - * @return true - username/password found, false - assume some other auth data reference was provided. + * Helper method gets the id of a configuration element, or generates a unique id based on that element + */ + private String getElementId(ConfigElement element) { + return element.getId() == null ? // + element.getClass().getSimpleName() + "@" + Integer.toHexString(element.toString().hashCode()) : // + element.getId(); + } + + + /** + *
+     * Determine if we can update AuthData based on the following checks:
+     * 
+     * 1. The original AuthData set fat.modify to true (not null, false, or any other string)
+     * 2. The original AuthData did not contain the username ${env.DB_USER} and password ${env.DB_PASS}
+     * 
+ * + * @param props The original AuthData element + * @return true if the original AuthData can be updated, false otherwise. */ - private static AuthLocation whereIsAuthData(DataSource datasource) { - //Ensure server config doesn't container multiple Properties or AuthDatas per datasource. Uncommon. - if (datasource.getDataSourceProperties().size() > 1 || datasource.getContainerAuthDatas().size() > 1) { - throw new RuntimeException("Database rotation cannot handle cases where multiple DataSource property or Container AuthData elements exist." - + " This is uncommon configuration and shouldn't be tested on a rotating basis."); + private boolean canUpdate(AuthData ad) { + if(ad.getFatModify() == null || // + ad.getFatModify().equalsIgnoreCase("false")) { + return false; } - - for (DataSourceProperties props : datasource.getDataSourceProperties()) { - if (props.getUser() != null || props.getPassword() != null) { - return AuthLocation.inProperties.withID(props.getId()); - } + + if(ad.getUser().equalsIgnoreCase(toReplacementString(USER_KEY)) && // + ad.getPassword().equalsIgnoreCase(toReplacementString(PASS_KEY))) { + return false; } - - if (datasource.getContainerAuthDatas().size() == 1) { - return AuthLocation.inEmbeddedAuthData.withID(datasource.getContainerAuthDatas().get(0).getId()); + + return ad.getFatModify().equalsIgnoreCase("true"); + } + + /** + *
+     * Determine if we can update DataSourceProperties based on the following checks:
+     * 
+     * 1. The original DataSourceProperties contained a username and password
+     * 2. The original DataSourceProperties did not contain the username ${env.DB_USER} and password ${env.DB_PASS}
+     * 
+ * + * @param props The original DataSourceProperties element + * @return true if the original DataSourceProperties can be updated, false otherwise. + */ + private boolean canUpdate(DataSourceProperties props) { + if(props.getUser() == null && props.getPassword() == null) { + return false; } - - if (datasource.getContainerAuthDataRef() != null) { - return AuthLocation.inAuthDataRef.withID(datasource.getContainerAuthDataRef()); + + if(props.getUser().equalsIgnoreCase(toReplacementString(USER_KEY)) && // + props.getPassword().equalsIgnoreCase(toReplacementString(PASS_KEY))) { + return false; } - - return AuthLocation.none.withID("NONE"); + + return true; } - // It is possible to have multiple DataSource / AuthData elements with null IDs. - // Make sure they all get put into a HashMap so we can modify them. - @SuppressWarnings("serial") - private static final class HashMapWithNullKeys extends HashMap implements Map { - int nullCounter = 0; - - @Override - public V put(String key, V value) { - if (key == null) { - key = "null" + nullCounter++; - } - return super.put(key, value); + /** + *
+     * Authentication data for a dataSource can be found in any of the following locations:
+     * 
+     * 1. Within the {@code } element of the dataSource.
+     * 2. Within the {@code } element of the dataSource.
+     * 3. Within the {@code  attribute of the dataSource
+     * 
+     * 
+ * + * @param ds The dataSource + * @return a set of AuthDatas that the dataSource references or contains. + */ + private Set findAuthDataLocations(DataSource ds) { + + Set authDataElements = new HashSet<>(); + + authDataElements.addAll(ds.getContainerAuthDatas()); + + if(ds.getContainerAuthDataRef() != null) { + authDataElements.add(serverClone.getAuthDataElements().getById(ds.getContainerAuthDataRef())); + } + + if(ds.getRecoveryAuthDataRef() != null) { + authDataElements.add(serverClone.getAuthDataElements().getById(ds.getRecoveryAuthDataRef())); } + + return authDataElements; } } From 0cc35c3540a125671182b19831cecd62499d0778 Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Tue, 12 Nov 2024 16:21:50 -0600 Subject: [PATCH 3/8] test: add test for DefaultDataSource --- .../resources/init-sqlserver.sql | 14 +++ .../test/jakarta/data/ddlgen/DDLGenTest.java | 54 ++++++---- .../server.xml | 99 ++++++++++++------- .../resources/WEB-INF/ibm-web-bnd.xml | 23 ----- .../src/test/jakarta/data/ddlgen/web/Car.java | 44 +++++++++ .../test/jakarta/data/ddlgen/web/Cars.java | 23 +++++ .../data/ddlgen/web/DDLGenTestServlet.java | 45 ++++++--- 7 files changed, 213 insertions(+), 89 deletions(-) delete mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/resources/WEB-INF/ibm-web-bnd.xml create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Car.java create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Cars.java diff --git a/dev/fattest.simplicity/resources/init-sqlserver.sql b/dev/fattest.simplicity/resources/init-sqlserver.sql index 97d3741f1b6..abf85d1a42c 100644 --- a/dev/fattest.simplicity/resources/init-sqlserver.sql +++ b/dev/fattest.simplicity/resources/init-sqlserver.sql @@ -4,5 +4,19 @@ CREATE DATABASE TEST; -- Enable XA connections EXEC sp_sqljdbc_xa_install; +-- Enable all users to use distributed transactions +GRANT EXECUTE ON xp_sqljdbc_xa_init to public +GRANT EXECUTE ON xp_sqljdbc_xa_start to public +GRANT EXECUTE ON xp_sqljdbc_xa_end to public +GRANT EXECUTE ON xp_sqljdbc_xa_prepare to public +GRANT EXECUTE ON xp_sqljdbc_xa_commit to public +GRANT EXECUTE ON xp_sqljdbc_xa_rollback to public +GRANT EXECUTE ON xp_sqljdbc_xa_recover to public +GRANT EXECUTE ON xp_sqljdbc_xa_forget to public +GRANT EXECUTE ON xp_sqljdbc_xa_rollback_ex to public +GRANT EXECUTE ON xp_sqljdbc_xa_forget_ex to public +GRANT EXECUTE ON xp_sqljdbc_xa_prepare_ex to public +GRANT EXECUTE ON xp_sqljdbc_xa_init_ex to public + -- Allow snapshot isolation ALTER DATABASE TEST SET ALLOW_SNAPSHOT_ISOLATION ON \ No newline at end of file diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java index 987e5edf8ea..a2f00008a27 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java @@ -52,14 +52,13 @@ public class DDLGenTest extends FATServletClient { @BeforeClass public static void setUp() throws Exception { // Create an additional user and schema on database (matches authData id="dbuser") - createUserAndSchema(FATSuite.testContainer, "dbuser", "DBuserPassw0rd"); + String preamble = createUserAndSchema(FATSuite.testContainer, "dbuser", "DBuserPassw0rd"); - // Get driver type - DatabaseContainerType type = DatabaseContainerType.valueOf(FATSuite.testContainer); - server.addEnvVar("DB_DRIVER", type.getDriverName()); - - // Set up server DataSource properties - DatabaseContainerUtil.setupDataSourceDatabaseProperties(server, FATSuite.testContainer); + DatabaseContainerUtil.build(server, FATSuite.testContainer) + .withDriverVariable() + .withAuthVariables("dbuser", "DBuserPassw0rd") + .withDatabaseProperties() + .modify(); WebArchive war = ShrinkHelper.buildDefaultApp("DDLGenTestApp", "test.jakarta.data.ddlgen.web"); ShrinkHelper.exportAppToServer(server, war); @@ -69,12 +68,15 @@ public static void setUp() throws Exception { DDLGenScriptResult result = DDLGenScript.build(server) .execute() .assertSuccessful() + .assertDDLFile("application[DDLGenTestApp].module[DDLGenTestApp.war].databaseStore[java.comp.DefaultDataSource]_JakartaData.ddl") .assertDDLFile("databaseStore[TestDataStore]_JakartaData.ddl"); + String scripts = String.join(",", result.getFileLocations()); + runTest(server, "DDLGenTestApp/DDLGenTestServlet", "executeDDL" + - "&scripts=" + String.join(",", result.getFileLocations()) + - "&withDatabaseStore=TestDataStore" + - "&usingDataSource=java:app/env/adminDataSourceRef"); + "&scripts=" + scripts + + "&preamble=" + preamble.replace(" ", "]") + + "&usingDataSource=jdbc/AdminDataSource"); } @@ -91,14 +93,17 @@ public static void tearDown() throws Exception { * @param user the user/schema to create (the schema will be named after the user) * @param pass the password for the user * @throws Exception if any database connection or query executions occur + * @return String preamble necessary for executing DDL statements as admin user in the database */ - private static void createUserAndSchema(JdbcDatabaseContainer testContainer, String user, String pass) throws Exception { + private static String createUserAndSchema(JdbcDatabaseContainer testContainer, String user, String pass) throws Exception { final DatabaseContainerType type = DatabaseContainerType.valueOf(testContainer); + String preamble = ""; + Log.entering(FATSuite.class, "createUserAndSchema", new Object[] { type, user, pass }); try { switch (type) { - case DB2: //working + case DB2: // Working - admin user will set schema to user final List results = new ArrayList<>(); final String connectDBPrefix = "db2 connect to " + testContainer.getDatabaseName() + "; "; @@ -115,14 +120,14 @@ private static void createUserAndSchema(JdbcDatabaseContainer testContainer, for (ExecResult result : results) { assertEquals("Unexpected result from command on DB2 container, std.err: " + result.getStderr(), 0, result.getExitCode()); } + + preamble = "set schema " + user; break; - case Derby: - // Unnecessary + case Derby: // Working - uses user instead of admin when executing ddl statements break; - case DerbyClient: - // Unnecessary + case DerbyClient: // Unnecessary break; - case Oracle: //working + case Oracle: // Working - admin user will set schema to user // On oracle a user and a schema are one and the same, so just create a user try (Connection con = testContainer.createConnection(""); Statement stmt = con.createStatement()) { stmt.executeUpdate("alter session set \"_ORACLE_SCRIPT\"=true"); @@ -131,35 +136,42 @@ private static void createUserAndSchema(JdbcDatabaseContainer testContainer, stmt.executeUpdate("grant connect, create session to " + user); stmt.executeUpdate("grant unlimited tablespace to " + user); } + + preamble = "alter session set current_schema=" + user; break; - case Postgres: //working + case Postgres: // Working - admin and user share public schema when none is defined try (Connection con = testContainer.createConnection(""); Statement stmt = con.createStatement()) { stmt.executeUpdate("create user " + user + " with password '" + pass + "'"); stmt.executeUpdate("grant connect on database " + testContainer.getDatabaseName() + " to " + user + " with grant option"); stmt.executeUpdate("create schema " + user); stmt.executeUpdate("alter default privileges in schema " + user + " grant all privileges on tables to " + user); + stmt.executeUpdate("alter default privileges in schema public " + + " grant all privileges on tables to " + user); stmt.executeUpdate("grant usage on schema " + user + " to " + user); } break; - case SQLServer: //working + case SQLServer: // Working - admin and user share dbo schema when none is defined //TODO remove once bug is fixed: https://github.com/testcontainers/testcontainers-java/pull/9463 //replace with: testContainer.createConnection(";databaseName=TEST") testContainer.withUrlParam("databaseName", "TEST"); try (Connection con = testContainer.createConnection(""); Statement stmt = con.createStatement()) { stmt.executeUpdate("create login " + user + " with password = '" + pass + "'"); - stmt.executeUpdate("create user " + user + " from login " + user); + stmt.executeUpdate("create user " + user + " from login " + user + " with default_schema=dbo"); stmt.executeUpdate("grant connect to " + user); stmt.executeUpdate("create schema " + user); stmt.executeUpdate("grant control on schema :: " + user + " to " + user); + stmt.executeUpdate("grant control on schema :: dbo to " + user); + stmt.executeUpdate("grant create table to " + user); } break; default: throw new RuntimeException("Unknown database container type"); } } finally { - Log.exiting(FATSuite.class, "createUserAndSchema"); + Log.exiting(FATSuite.class, "createUserAndSchema", preamble); } + return preamble; } } diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml index c5d20203464..e1e42eab58d 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml +++ b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml @@ -12,46 +12,79 @@ --> - - - data-1.0 - persistence-3.2 - - - localConnector-1.0 - - - componenttest-2.0 - servlet-6.1 - + + + data-1.0 + persistence-3.2 + + + localConnector-1.0 + + + componenttest-2.0 + servlet-6.1 + + + + + + test.jakarta.data.ddlgen.web + false + + + + + + + + + + + + - + + + + + + + + + + + - - test.jakarta.data.ddlgen.web - + + + - - + + + + - + + + + - - - - - + - - - + + + - + \ No newline at end of file diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/resources/WEB-INF/ibm-web-bnd.xml b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/resources/WEB-INF/ibm-web-bnd.xml deleted file mode 100644 index 3e8ef06e9ba..00000000000 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/resources/WEB-INF/ibm-web-bnd.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Car.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Car.java new file mode 100644 index 00000000000..54de61a5802 --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Car.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class Car { + @Id + public String vin; + + public String make; + + public String model; + + public int modelYear; + + public int odometer; + + public float price; + + public static Car of(String vin, String make, String model, int modelYear, int odometer, float price) { + Car inst = new Car(); + inst.vin = vin; + inst.make = make; + inst.model = model; + inst.modelYear = modelYear; + inst.odometer = odometer; + inst.price = price; + return inst; + } + +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Cars.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Cars.java new file mode 100644 index 00000000000..c95e5467402 --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Cars.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Repository; + +/** + * Repository for java:comp/DefaultDataSource + */ +@Repository +public interface Cars extends BasicRepository { +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java index 796904c8bab..373a476e967 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.Objects; -import jakarta.annotation.Resource; import jakarta.inject.Inject; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServletRequest; @@ -39,13 +38,12 @@ @WebServlet("/*") public class DDLGenTestServlet extends FATServlet { - @Resource(name = "java:app/env/adminDataSourceRef", - lookup = "jdbc/TestDataSource") - DataSource adminDataSource; - @Inject Parts parts; + @Inject + Cars cars; + /** * Executes the DDL in the database as a database admin. * This method is intentionally not annotated with @Test. @@ -54,15 +52,12 @@ public class DDLGenTestServlet extends FATServlet { */ public void executeDDL(HttpServletRequest request, HttpServletResponse response) throws Exception { List files = Arrays.asList(Objects.requireNonNull(request.getParameter("scripts")).split(",")); - String withDatabaseStore = Objects.requireNonNull(request.getParameter("withDatabaseStore")); String usingDataSource = Objects.requireNonNull(request.getParameter("usingDataSource")); + String preamble = Objects.requireNonNull(request.getParameter("preamble")).replace("]", " "); - DataSource ds = InitialContext.doLookup(usingDataSource); + DataSource admin = InitialContext.doLookup(usingDataSource); for (String file : files) { - if (!file.contains(withDatabaseStore)) - continue; - try (BufferedReader reader = new BufferedReader(new FileReader(file))) { System.out.println("Execute DDL file: " + file); String line; @@ -70,13 +65,38 @@ public void executeDDL(HttpServletRequest request, HttpServletResponse response) if (line.isBlank() || line.equals("EXIT;")) continue; - System.out.println(" Execute SQL Statement: " + line); - try (Connection con = ds.getConnection(); Statement stmt = con.createStatement()) { + try (Connection con = admin.getConnection(); Statement stmt = con.createStatement()) { + if (!preamble.isBlank()) { + System.out.println(" Execute Preamble: " + preamble); + stmt.executeUpdate(preamble); + } + System.out.println(" Execute SQL Statement: " + line); stmt.execute(line); } } } } + + } + + /** + * Attempt to insert, find, and delete a row in the Cars table + * which was created via execution of generated ddl files + */ + @Test + public void testSaveToDefaultDatabase() { + assertEquals("Table should not have any starting values", 0, cars.findAll().count()); + + String id = cars.save(Car.of("1234", "Honda", "Civic", 2014, 89452, 7500)).vin; + + Car result = cars.findById(id).orElseThrow(); + assertEquals("Honda", result.make); + assertEquals("Civic", result.model); + assertEquals(2014, result.modelYear); + assertEquals(89452, result.odometer); + assertEquals(7500, result.price, 0.1); + + cars.delete(result); } /** @@ -118,4 +138,5 @@ public void testSaveAndFindByEmbeddedId() { parts.deleteAll(List.of(part2, part3)); } + } From 68838c94215a2703e94b2ea2cbdde4a831cc9233 Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Wed, 13 Nov 2024 13:21:36 -0600 Subject: [PATCH 4/8] test: add test for dataSource id --- .../test/jakarta/data/ddlgen/DDLGenTest.java | 1 + .../server.xml | 5 ++ .../jakarta/data/ddlgen/web/Automobile.java | 46 +++++++++++++++++++ .../src/test/jakarta/data/ddlgen/web/Car.java | 1 - .../data/ddlgen/web/DDLGenTestServlet.java | 39 +++++++++++++++- .../test/jakarta/data/ddlgen/web/Truck.java | 35 ++++++++++++++ .../test/jakarta/data/ddlgen/web/Trucks.java | 24 ++++++++++ 7 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Automobile.java create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Truck.java create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Trucks.java diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java index a2f00008a27..1002bb2f314 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java @@ -69,6 +69,7 @@ public static void setUp() throws Exception { .execute() .assertSuccessful() .assertDDLFile("application[DDLGenTestApp].module[DDLGenTestApp.war].databaseStore[java.comp.DefaultDataSource]_JakartaData.ddl") + .assertDDLFile("application[DDLGenTestApp].databaseStore[TestDataSource]_JakartaData.ddl") .assertDDLFile("databaseStore[TestDataStore]_JakartaData.ddl"); String scripts = String.join(",", result.getFileLocations()); diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml index e1e42eab58d..17677483bd2 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml +++ b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml @@ -55,6 +55,11 @@
+ + + + diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Automobile.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Automobile.java new file mode 100644 index 00000000000..547fc8b79eb --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Automobile.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public class Automobile { + @Id + public String vin; + + public String make; + + public String model; + + public int modelYear; + + public int odometer; + + public float price; + + public static Automobile of(String vin, String make, String model, int modelYear, int odometer, float price) { + Automobile inst = new Automobile(); + inst.vin = vin; + inst.make = make; + inst.model = model; + inst.modelYear = modelYear; + inst.odometer = odometer; + inst.price = price; + return inst; + } +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Car.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Car.java index 54de61a5802..2c4a425751d 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Car.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Car.java @@ -40,5 +40,4 @@ public static Car of(String vin, String make, String model, int modelYear, int o inst.price = price; return inst; } - } diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java index 373a476e967..6c8381cde50 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java @@ -13,10 +13,12 @@ package test.jakarta.data.ddlgen.web; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import java.io.BufferedReader; import java.io.FileReader; import java.sql.Connection; +import java.sql.ResultSet; import java.sql.Statement; import java.util.Arrays; import java.util.List; @@ -28,6 +30,7 @@ import jakarta.servlet.http.HttpServletResponse; import javax.naming.InitialContext; +import javax.naming.NamingException; import javax.sql.DataSource; import org.junit.Test; @@ -44,6 +47,9 @@ public class DDLGenTestServlet extends FATServlet { @Inject Cars cars; + @Inject + Trucks trucks; + /** * Executes the DDL in the database as a database admin. * This method is intentionally not annotated with @Test. @@ -85,7 +91,7 @@ public void executeDDL(HttpServletRequest request, HttpServletResponse response) */ @Test public void testSaveToDefaultDatabase() { - assertEquals("Table should not have any starting values", 0, cars.findAll().count()); + assertEquals("Table car should not have any starting values", 0, cars.findAll().count()); String id = cars.save(Car.of("1234", "Honda", "Civic", 2014, 89452, 7500)).vin; @@ -99,6 +105,37 @@ public void testSaveToDefaultDatabase() { cars.delete(result); } + /** + * Attempt to insert, find, and delete a row in the Trucks table + * which was created via execution of generated ddl files + * + * @throws NamingException + */ + @Test + public void testSaveToDatasourceId() throws Exception { + assertEquals("Table truck should not have any starting values", 0, trucks.findAll().count()); + + String id = trucks.save(Truck.of("1234", "Honda", "Ridgeline", 2025, 10, 40150, 63.6)).vin; + + //Ensure no data was not put into the automobile table + DataSource testDataSource = InitialContext.doLookup("jdbc/TestDataSource"); + try (Connection con = testDataSource.getConnection(); Statement stmt = con.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT * FROM Automobile")) { + assertFalse("Truck element should have been saved to Truck table, not Automobile", rs.next()); //No data in table + } + } + + Truck result = trucks.findById(id).orElseThrow(); + assertEquals("Honda", result.make); + assertEquals("Ridgeline", result.model); + assertEquals(2025, result.modelYear); + assertEquals(10, result.odometer); + assertEquals(40150, result.price, 0.1); + assertEquals(63.6, result.bedLength, 0.01); + + trucks.delete(result); + } + /** * Modify and query for a record entity based on its composite id using * BasicRepository.save and BasicRepository.findById. diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Truck.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Truck.java new file mode 100644 index 00000000000..3e24ffe1e5c --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Truck.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.persistence.Entity; + +/** + * Inherits from Automobile, but has it's own table + */ +@Entity +public class Truck extends Automobile { + public double bedLength; + + public static Truck of(String vin, String make, String model, int modelYear, int odometer, float price, double bedLength) { + Truck inst = new Truck(); + inst.vin = vin; + inst.make = make; + inst.model = model; + inst.modelYear = modelYear; + inst.odometer = odometer; + inst.price = price; + inst.bedLength = bedLength; + return inst; + } +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Trucks.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Trucks.java new file mode 100644 index 00000000000..0d39f402829 --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Trucks.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Repository; + +/** + * Repository for datasource id TestDataSource + */ +@Repository(dataStore = "TestDataSource") +public interface Trucks extends BasicRepository { + +} From 9316b9352b5537ba2239b2c00debf778b0c236da Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Wed, 13 Nov 2024 14:16:39 -0600 Subject: [PATCH 5/8] test: add test for dataSoure jdniName --- .../test/jakarta/data/ddlgen/DDLGenTest.java | 1 + .../server.xml | 5 +++ .../test/jakarta/data/ddlgen/web/Auto.java | 43 +++++++++++++++++++ .../data/ddlgen/web/DDLGenTestServlet.java | 39 +++++++++++++++-- .../src/test/jakarta/data/ddlgen/web/Van.java | 35 +++++++++++++++ .../test/jakarta/data/ddlgen/web/Vans.java | 24 +++++++++++ 6 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Auto.java create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Van.java create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Vans.java diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java index 1002bb2f314..fcc94fac947 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java @@ -70,6 +70,7 @@ public static void setUp() throws Exception { .assertSuccessful() .assertDDLFile("application[DDLGenTestApp].module[DDLGenTestApp.war].databaseStore[java.comp.DefaultDataSource]_JakartaData.ddl") .assertDDLFile("application[DDLGenTestApp].databaseStore[TestDataSource]_JakartaData.ddl") + .assertDDLFile("application[DDLGenTestApp].databaseStore[jdbc.TestDataSourceJndi]_JakartaData.ddl") .assertDDLFile("databaseStore[TestDataStore]_JakartaData.ddl"); String scripts = String.join(",", result.getFileLocations()); diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml index 17677483bd2..ce7406d7b75 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml +++ b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml @@ -62,6 +62,11 @@
+ + + + diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Auto.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Auto.java new file mode 100644 index 00000000000..98e30a9e2bf --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Auto.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public class Auto { + @Id + public String vin; + + public String make; + + public String model; + + public int modelYear; + + public int odometer; + + public float price; + + public static Auto of(String vin, String make, String model, int modelYear, int odometer, float price) { + Auto inst = new Auto(); + inst.vin = vin; + inst.make = make; + inst.model = model; + inst.modelYear = modelYear; + inst.odometer = odometer; + inst.price = price; + return inst; + } +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java index 6c8381cde50..8fa692ddc21 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java @@ -14,11 +14,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.FileReader; import java.sql.Connection; import java.sql.ResultSet; +import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; import java.util.List; @@ -30,7 +32,6 @@ import jakarta.servlet.http.HttpServletResponse; import javax.naming.InitialContext; -import javax.naming.NamingException; import javax.sql.DataSource; import org.junit.Test; @@ -50,6 +51,9 @@ public class DDLGenTestServlet extends FATServlet { @Inject Trucks trucks; + @Inject + Vans vans; + /** * Executes the DDL in the database as a database admin. * This method is intentionally not annotated with @Test. @@ -108,8 +112,6 @@ public void testSaveToDefaultDatabase() { /** * Attempt to insert, find, and delete a row in the Trucks table * which was created via execution of generated ddl files - * - * @throws NamingException */ @Test public void testSaveToDatasourceId() throws Exception { @@ -136,6 +138,37 @@ public void testSaveToDatasourceId() throws Exception { trucks.delete(result); } + /** + * Attempt to insert, find, and delete a row in the van table + * which was created via execution of generated ddl files + */ + @Test + public void testSaveToDatasourceJndiName() throws Exception { + assertEquals("Table van should not have any starting values", 0, vans.findAll().count()); + + String id = vans.save(Van.of("1234", "Honda", "Odyssey", 2015, 120500, 10926, 8)).vin; + + //Ensure a table named auto was never created + DataSource testDataSource = InitialContext.doLookup("jdbc/TestDataSourceJndi"); + try (Connection con = testDataSource.getConnection(); Statement stmt = con.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT * FROM Auto")) { + fail("The table Auto should not have been created"); + } catch (SQLException e) { + //expected + } + } + + Van result = vans.findById(id).orElseThrow(); + assertEquals("Honda", result.make); + assertEquals("Odyssey", result.model); + assertEquals(2015, result.modelYear); + assertEquals(120500, result.odometer); + assertEquals(10926, result.price, 0.1); + assertEquals(8, result.seats); + + vans.delete(result); + } + /** * Modify and query for a record entity based on its composite id using * BasicRepository.save and BasicRepository.findById. diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Van.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Van.java new file mode 100644 index 00000000000..e1ee7833269 --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Van.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.persistence.Entity; + +/** + * Inherits from Auto, and shares a table + */ +@Entity +public class Van extends Auto { + public int seats; + + public static Van of(String vin, String make, String model, int modelYear, int odometer, float price, int seats) { + Van inst = new Van(); + inst.vin = vin; + inst.make = make; + inst.model = model; + inst.modelYear = modelYear; + inst.odometer = odometer; + inst.price = price; + inst.seats = seats; + return inst; + } +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Vans.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Vans.java new file mode 100644 index 00000000000..724ce102419 --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Vans.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Repository; + +/** + * Repository for datasource jndiName jdbc/TestDataSource + */ +@Repository(dataStore = "jdbc/TestDataSourceJndi") +public interface Vans extends BasicRepository { + +} From aa1a3b2cf7da73a31726a9fe001173ba593d9257 Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Wed, 13 Nov 2024 14:35:19 -0600 Subject: [PATCH 6/8] test: add test for dataSoure resource reference --- .../test/jakarta/data/ddlgen/DDLGenTest.java | 1 + .../server.xml | 10 +++++- .../data/ddlgen/web/DDLGenTestServlet.java | 28 +++++++++++++++ .../src/test/jakarta/data/ddlgen/web/SUV.java | 35 +++++++++++++++++++ .../test/jakarta/data/ddlgen/web/SUVs.java | 24 +++++++++++++ .../src/test/jakarta/data/ddlgen/web/Van.java | 2 +- 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/SUV.java create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/SUVs.java diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java index fcc94fac947..16739f45bab 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java @@ -71,6 +71,7 @@ public static void setUp() throws Exception { .assertDDLFile("application[DDLGenTestApp].module[DDLGenTestApp.war].databaseStore[java.comp.DefaultDataSource]_JakartaData.ddl") .assertDDLFile("application[DDLGenTestApp].databaseStore[TestDataSource]_JakartaData.ddl") .assertDDLFile("application[DDLGenTestApp].databaseStore[jdbc.TestDataSourceJndi]_JakartaData.ddl") + .assertDDLFile("application[DDLGenTestApp].databaseStore[java.app.env.jdbc.TestDataSourceResourceRef]_JakartaData.ddl") .assertDDLFile("databaseStore[TestDataStore]_JakartaData.ddl"); String scripts = String.join(",", result.getFileLocations()); diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml index ce7406d7b75..7726e031eaa 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml +++ b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml @@ -69,8 +69,16 @@ + + + + + - + + diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java index 8fa692ddc21..20460daad97 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Objects; +import jakarta.annotation.Resource; import jakarta.inject.Inject; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServletRequest; @@ -54,6 +55,12 @@ public class DDLGenTestServlet extends FATServlet { @Inject Vans vans; + @Inject + SUVs suvs; + + @Resource(name = "java:app/env/jdbc/TestDataSourceResourceRef", lookup = "jdbc/TestDataSourceResource") + DataSource TestDataSourceResourceRef; + /** * Executes the DDL in the database as a database admin. * This method is intentionally not annotated with @Test. @@ -169,6 +176,27 @@ public void testSaveToDatasourceJndiName() throws Exception { vans.delete(result); } + /** + * Attempt to insert, find, and delete a row in the SUV table + * which was created via execution of generated ddl files + */ + @Test + public void testSaveToDatasourceResourceRef() throws Exception { + assertEquals("Table SUV should not have any starting values", 0, suvs.findAll().count()); + + String id = suvs.save(SUV.of("1234", "Honda", "CR-V", 2007, 200400, 5509, true)).vin; + + SUV result = suvs.findById(id).orElseThrow(); + assertEquals("Honda", result.make); + assertEquals("CR-V", result.model); + assertEquals(2007, result.modelYear); + assertEquals(200400, result.odometer); + assertEquals(5509, result.price, 0.1); + assertEquals(true, result.hatchback); + + suvs.delete(result); + } + /** * Modify and query for a record entity based on its composite id using * BasicRepository.save and BasicRepository.findById. diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/SUV.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/SUV.java new file mode 100644 index 00000000000..d6c7481cc2a --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/SUV.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.persistence.Entity; + +/** + * Inherits from Auto which is not itself an entity but a mapped superclass + */ +@Entity +public class SUV extends Auto { + public boolean hatchback; + + public static SUV of(String vin, String make, String model, int modelYear, int odometer, float price, boolean hatchback) { + SUV inst = new SUV(); + inst.vin = vin; + inst.make = make; + inst.model = model; + inst.modelYear = modelYear; + inst.odometer = odometer; + inst.price = price; + inst.hatchback = hatchback; + return inst; + } +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/SUVs.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/SUVs.java new file mode 100644 index 00000000000..d903bfcc5d8 --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/SUVs.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Repository; + +/** + * Repository for datasource jndiName jdbc/TestDataSource + */ +@Repository(dataStore = "java:app/env/jdbc/TestDataSourceResourceRef") +public interface SUVs extends BasicRepository { + +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Van.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Van.java index e1ee7833269..3684cb922c0 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Van.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Van.java @@ -15,7 +15,7 @@ import jakarta.persistence.Entity; /** - * Inherits from Auto, and shares a table + * Inherits from Auto which is not itself an entity but a mapped superclass */ @Entity public class Van extends Auto { From 30e8c50ceea23e2db2ff8ad831385854e49dd99c Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Wed, 13 Nov 2024 15:52:30 -0600 Subject: [PATCH 7/8] test: add test for persistence unit resource reference --- .../topology/utils/DDLGenScript.java | 16 ++++++ .../test/jakarta/data/ddlgen/DDLGenTest.java | 1 + .../server.xml | 7 ++- .../WEB-INF/classes/META-INF/persistence.xml | 34 ++++++++++++ .../data/ddlgen/web/DDLGenTestServlet.java | 52 +++++++++++++++++++ .../test/jakarta/data/ddlgen/web/Mobile.java | 48 +++++++++++++++++ .../test/jakarta/data/ddlgen/web/Sedan.java | 37 +++++++++++++ .../test/jakarta/data/ddlgen/web/Sedans.java | 24 +++++++++ .../test/jakarta/data/ddlgen/web/Vans.java | 2 +- 9 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/resources/WEB-INF/classes/META-INF/persistence.xml create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Mobile.java create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Sedan.java create mode 100644 dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Sedans.java diff --git a/dev/fattest.simplicity/src/componenttest/topology/utils/DDLGenScript.java b/dev/fattest.simplicity/src/componenttest/topology/utils/DDLGenScript.java index dbea714cbf4..17f35ba5137 100644 --- a/dev/fattest.simplicity/src/componenttest/topology/utils/DDLGenScript.java +++ b/dev/fattest.simplicity/src/componenttest/topology/utils/DDLGenScript.java @@ -10,6 +10,7 @@ package componenttest.topology.utils; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.File; @@ -277,6 +278,21 @@ public DDLGenScriptResult assertDDLFiles(List expectedFileNames) { return this; } + /** + * Asserts that none of the generated DDL files in the DDL directory contains the provided substring + * + * @param unexpectedSubstring a substring you do not expect to find in any DDL file names + * @return this + */ + public DDLGenScriptResult assertNoDDLFileLike(String unexpectedSubstring) { + for (String observedFileName : getFileNames()) { + assertFalse("The generated DDL file " + observedFileName + " contained the unexpected substring " + unexpectedSubstring, + observedFileName.contains(unexpectedSubstring)); + } + + return this; + } + ///// Terminations ///// /** diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java index 16739f45bab..885fcca158d 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java @@ -72,6 +72,7 @@ public static void setUp() throws Exception { .assertDDLFile("application[DDLGenTestApp].databaseStore[TestDataSource]_JakartaData.ddl") .assertDDLFile("application[DDLGenTestApp].databaseStore[jdbc.TestDataSourceJndi]_JakartaData.ddl") .assertDDLFile("application[DDLGenTestApp].databaseStore[java.app.env.jdbc.TestDataSourceResourceRef]_JakartaData.ddl") + .assertNoDDLFileLike("java.app.env.persistence.MyPersistenceUnitRef") .assertDDLFile("databaseStore[TestDataStore]_JakartaData.ddl"); String scripts = String.join(",", result.getFileLocations()); diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml index 7726e031eaa..1e2b32b84dd 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml +++ b/dev/io.openliberty.data.internal_fat_ddlgen/publish/servers/io.openliberty.data.internal.fat.ddlgen/server.xml @@ -81,7 +81,12 @@ --> - + + + + + + diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/resources/WEB-INF/classes/META-INF/persistence.xml b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/resources/WEB-INF/classes/META-INF/persistence.xml new file mode 100644 index 00000000000..8f97b49280c --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/resources/WEB-INF/classes/META-INF/persistence.xml @@ -0,0 +1,34 @@ + + + + + + java:app/env/jdbc/TestDataSourcePersistenceRef + test.jakarta.data.ddlgen.web.Mobile + test.jakarta.data.ddlgen.web.Sedan + + + + + + + + + \ No newline at end of file diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java index 20460daad97..6dde7401bc1 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/DDLGenTestServlet.java @@ -14,6 +14,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.BufferedReader; @@ -28,6 +29,8 @@ import jakarta.annotation.Resource; import jakarta.inject.Inject; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceUnit; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -58,9 +61,19 @@ public class DDLGenTestServlet extends FATServlet { @Inject SUVs suvs; + @Inject + Sedans sedans; + @Resource(name = "java:app/env/jdbc/TestDataSourceResourceRef", lookup = "jdbc/TestDataSourceResource") DataSource TestDataSourceResourceRef; + @Resource(name = "java:app/env/jdbc/TestDataSourcePersistenceRef", lookup = "jdbc/TestDataSourcePersistence") + DataSource TestDataSourcePersistenceRef; + + @PersistenceUnit(name = "java:app/env/persistence/MyPersistenceUnitRef", + unitName = "MyPersistenceUnit") + EntityManagerFactory emf; + /** * Executes the DDL in the database as a database admin. * This method is intentionally not annotated with @Test. @@ -197,6 +210,45 @@ public void testSaveToDatasourceResourceRef() throws Exception { suvs.delete(result); } + /** + * Attempt to insert, find, and delete a row in the Mobile table + */ + @Test + public void testSaveToPersistenceUnitRef() throws Exception { + assertEquals("Table Mobile should not have any starting values", 0, sedans.findAll().count()); + + String id = sedans.save(Sedan.of("1234", "Honda", "Accord", 2022, 20000, 21227, 4)).vin; + + //Ensure the Sedan entity was put into the table Mobile + DataSource testDataSource = InitialContext.doLookup("java:app/env/jdbc/TestDataSourcePersistenceRef"); + try (Connection con = testDataSource.getConnection(); Statement stmt = con.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT * FROM Mobile")) { + assertTrue(rs.next()); + assertEquals("sedan", rs.getString("type")); + assertFalse(rs.next()); + } + } + + //Ensure the no table Sedan was created + try (Connection con = testDataSource.getConnection(); Statement stmt = con.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT * FROM Sedan")) { + fail("Table sedan should not have been created"); + } catch (SQLException e) { + //expected + } + } + + Sedan result = sedans.findById(id).orElseThrow(); + assertEquals("Honda", result.make); + assertEquals("Accord", result.model); + assertEquals(2022, result.modelYear); + assertEquals(20000, result.odometer); + assertEquals(21227, result.price, 0.1); + assertEquals(4, result.doors); + + sedans.delete(result); + } + /** * Modify and query for a record entity based on its composite id using * BasicRepository.save and BasicRepository.findById. diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Mobile.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Mobile.java new file mode 100644 index 00000000000..4d5e1e42fed --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Mobile.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +@Entity +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "type") +public class Mobile { + @Id + public String vin; + + public String make; + + public String model; + + public int modelYear; + + public int odometer; + + public float price; + + public static Automobile of(String vin, String make, String model, int modelYear, int odometer, float price) { + Automobile inst = new Automobile(); + inst.vin = vin; + inst.make = make; + inst.model = model; + inst.modelYear = modelYear; + inst.odometer = odometer; + inst.price = price; + return inst; + } +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Sedan.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Sedan.java new file mode 100644 index 00000000000..ab7bf176aaf --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Sedan.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; + +/** + * Inherits from Mobile and shares a table. + */ +@Entity +@DiscriminatorValue(value = "sedan") +public class Sedan extends Mobile { + public int doors; + + public static Sedan of(String vin, String make, String model, int modelYear, int odometer, float price, int doors) { + Sedan inst = new Sedan(); + inst.vin = vin; + inst.make = make; + inst.model = model; + inst.modelYear = modelYear; + inst.odometer = odometer; + inst.price = price; + inst.doors = doors; + return inst; + } +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Sedans.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Sedans.java new file mode 100644 index 00000000000..8d15b8bc265 --- /dev/null +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Sedans.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package test.jakarta.data.ddlgen.web; + +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Repository; + +/** + * Repository for persistence unit resource reference + */ +@Repository(dataStore = "java:app/env/persistence/MyPersistenceUnitRef") +public interface Sedans extends BasicRepository { + +} diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Vans.java b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Vans.java index 724ce102419..9080d5de9f2 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Vans.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/test-applications/DDLGenTestApp/src/test/jakarta/data/ddlgen/web/Vans.java @@ -16,7 +16,7 @@ import jakarta.data.repository.Repository; /** - * Repository for datasource jndiName jdbc/TestDataSource + * Repository for datasource jndiName jdbc/TestDataSourceJndi */ @Repository(dataStore = "jdbc/TestDataSourceJndi") public interface Vans extends BasicRepository { From 594f78bc09fbf171d948ef5106f49c4ef53dd9c4 Mon Sep 17 00:00:00 2001 From: Kyle Aure Date: Thu, 14 Nov 2024 14:05:28 -0600 Subject: [PATCH 8/8] fix: build failures due to database specific permissions --- .../fat/src/test/jakarta/data/ddlgen/DDLGenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java index 885fcca158d..f3e1bcae223 100644 --- a/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java +++ b/dev/io.openliberty.data.internal_fat_ddlgen/fat/src/test/jakarta/data/ddlgen/DDLGenTest.java @@ -137,7 +137,7 @@ private static String createUserAndSchema(JdbcDatabaseContainer testContainer stmt.executeUpdate("alter session set \"_ORACLE_SCRIPT\"=true"); stmt.executeUpdate("alter user " + testContainer.getUsername() + " quota unlimited on users"); stmt.executeUpdate("create user " + user + " identified by " + pass); - stmt.executeUpdate("grant connect, create session to " + user); + stmt.executeUpdate("grant connect, create session, create table to " + user); stmt.executeUpdate("grant unlimited tablespace to " + user); } @@ -152,7 +152,7 @@ private static String createUserAndSchema(JdbcDatabaseContainer testContainer " grant all privileges on tables to " + user); stmt.executeUpdate("alter default privileges in schema public " + " grant all privileges on tables to " + user); - stmt.executeUpdate("grant usage on schema " + user + " to " + user); + stmt.executeUpdate("grant all on schema " + user + " to " + user); } break; case SQLServer: // Working - admin and user share dbo schema when none is defined