From 2a536a3f47d2372c9b008812ede91960ae71fe00 Mon Sep 17 00:00:00 2001 From: Iliyan Velichkov Date: Fri, 9 Aug 2024 11:16:18 +0300 Subject: [PATCH] DataSource refactoring + Snowflake fixes (#4204) * more cleanup Signed-off-by: Iliyan Velichkov * rollback Signed-off-by: Iliyan Velichkov * set connection test query Signed-off-by: Iliyan Velichkov * remove explicit token property loading Signed-off-by: Iliyan Velichkov * add DatabaseType to dirigible data sources Signed-off-by: Iliyan Velichkov * rename DatabaseType to DatabaseSystem since it is already used move to common package the type Signed-off-by: Iliyan Velichkov * dont add props twice Signed-off-by: Iliyan Velichkov * do not overwrite snowflake properties Signed-off-by: Iliyan Velichkov * remove user Signed-off-by: Iliyan Velichkov * rollback Signed-off-by: Iliyan Velichkov * refactor SqlDialectFactory use DirigibleDataSource for optimization Signed-off-by: Iliyan Velichkov * refactoring Signed-off-by: Iliyan Velichkov * add some optimizations Signed-off-by: Iliyan Velichkov * add wrapper for db connections + optimize dialect loading Signed-off-by: Iliyan Velichkov * fix Signed-off-by: Iliyan Velichkov * fix build Signed-off-by: Iliyan Velichkov * schedule snowflake datasource destroy after 9 mins since created Signed-off-by: Iliyan Velichkov * refactoring Signed-off-by: Iliyan Velichkov * code formatting Signed-off-by: Iliyan Velichkov * refact Signed-off-by: Iliyan Velichkov * cleanup Signed-off-by: Iliyan Velichkov * refactor connection enhance logic Signed-off-by: Iliyan Velichkov * remove logger Signed-off-by: Iliyan Velichkov * more cleanup Signed-off-by: Iliyan Velichkov * DatabaseSystemDeterminer refactoring Signed-off-by: Iliyan Velichkov * set dirigible loggers to debug in integration tests Signed-off-by: Iliyan Velichkov * code formating Signed-off-by: Iliyan Velichkov * add TS API Signed-off-by: Iliyan Velichkov * refactoring Signed-off-by: Iliyan Velichkov * fix Signed-off-by: Iliyan Velichkov * reduce logging Signed-off-by: Iliyan Velichkov * fix postgresql IT stability - provision tenant and then publish the test project Signed-off-by: Iliyan Velichkov * increase timeout Signed-off-by: Iliyan Velichkov * add DIRIGIBLE_SNOWFLAKE_DATA_SOURCE_LIFESPAN_SECONDS config Signed-off-by: Iliyan Velichkov --------- Signed-off-by: Iliyan Velichkov --- .../components/api/db/DatabaseFacade.java | 12 +- .../dirigible/modules/src/db/database.ts | 119 ++++--- .../database/ConnectionEnhancer.java | 20 ++ .../database/DatabaseConfigurator.java | 22 ++ .../components/database/DatabaseSystem.java | 48 +++ .../database/DatabaseSystemAware.java | 9 + .../database/DatabaseSystemDeterminer.java | 114 +++++++ .../database/DirigibleConnection.java | 7 + .../database/DirigibleDataSource.java | 16 + .../DatabaseSystemDeterminerTest.java | 133 ++++++++ .../export/service/DataExportService.java | 36 +- .../export/service/DatabaseExportService.java | 46 ++- .../export/service/DataImportService.java | 18 +- .../helpers/DatabaseMetadataHelper.java | 44 ++- .../load/DataSourceMetadataLoader.java | 41 +-- ...aSourceInitializerContributorSnowpark.java | 93 ------ .../snowpark/SnowflakeConnectionEnhancer.java | 22 ++ .../SnowflakeDatabaseConfigurator.java | 97 ++++++ .../manager/DataSourceInitializer.java | 168 +++++----- .../DataSourceInitializerContributor.java | 28 -- .../sources/manager/DataSourcesManager.java | 7 +- .../manager/DirigibleConnectionImpl.java | 307 ++++++++++++++++++ .../manager/DirigibleDataSourceFactory.java | 33 ++ ...urce.java => DirigibleDataSourceImpl.java} | 105 +++--- .../ODataDatabaseMetadataUtilTest.java | 36 +- .../commons/config/DirigibleConfig.java | 2 + .../sql/dialects/h2/H2SqlDialectProvider.java | 10 +- modules/database/database-sql-hana/pom.xml | 4 + .../dialects/hana/HanaConnectionEnhancer.java | 40 +++ .../hana/HanaDatabaseConfigurator.java | 33 ++ .../dialects/hana/HanaSqlDialectProvider.java | 10 +- .../mariadb/MariaDBSqlDialectProvider.java | 10 +- .../mongodb/MongoDBSqlDialectProvider.java | 10 +- .../mysql/MySQLSqlDialectProvider.java | 10 +- .../postgres/PostgresSqlDialectProvider.java | 10 +- .../SnowflakeSqlDialectProvider.java | 10 +- .../sybase/SybaseSqlDialectProvider.java | 10 +- modules/database/database-sql/pom.xml | 5 + .../database/sql/ISqlDialectProvider.java | 9 +- .../sql/dialects/SqlDialectFactory.java | 87 +++-- modules/database/pom.xml | 12 +- .../sql/builder/SQLSelectBuilderTest.java | 69 ++-- modules/odata/odata-core/pom.xml | 5 + .../engine/odata2/sql/builder/SQLContext.java | 91 +----- .../odata2/sql/clause/SQLGroupByClause.java | 4 +- .../odata2/sql/clause/SQLOrderByClause.java | 11 +- .../odata2/sql/clause/SQLSelectClause.java | 8 +- .../sql/processor/AbstractSQLProcessor.java | 2 +- .../integration/tests/DirigibleCleaner.java | 23 +- .../integration/tests/TenantCreator.java | 5 +- .../integration/tests/api/java/CsvimIT.java | 9 +- .../tests/ui/tests/BPMStarterTemplateIT.java | 2 +- .../tests/ui/tests/MultitenancyIT.java | 2 +- .../tests/ui/tests/OrderedTestSuite.java | 3 +- .../src/test/resources/logback-test.xml | 75 +++++ 55 files changed, 1471 insertions(+), 691 deletions(-) create mode 100644 components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/ConnectionEnhancer.java create mode 100644 components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseConfigurator.java create mode 100644 components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystem.java create mode 100644 components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystemAware.java create mode 100644 components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystemDeterminer.java create mode 100644 components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DirigibleConnection.java create mode 100644 components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DirigibleDataSource.java create mode 100644 components/core/core-database/src/test/java/org/eclipse/dirigible/components/database/DatabaseSystemDeterminerTest.java delete mode 100644 components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/DataSourceInitializerContributorSnowpark.java create mode 100644 components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/SnowflakeConnectionEnhancer.java create mode 100644 components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/SnowflakeDatabaseConfigurator.java delete mode 100644 components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourceInitializerContributor.java create mode 100644 components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleConnectionImpl.java create mode 100644 components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleDataSourceFactory.java rename components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/{ManagedDataSource.java => DirigibleDataSourceImpl.java} (62%) create mode 100644 modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaConnectionEnhancer.java create mode 100644 modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaDatabaseConfigurator.java create mode 100644 tests/tests-integrations/src/test/resources/logback-test.xml diff --git a/components/api/api-database/src/main/java/org/eclipse/dirigible/components/api/db/DatabaseFacade.java b/components/api/api-database/src/main/java/org/eclipse/dirigible/components/api/db/DatabaseFacade.java index 9df5d5ebac2..1efb73f30fa 100644 --- a/components/api/api-database/src/main/java/org/eclipse/dirigible/components/api/db/DatabaseFacade.java +++ b/components/api/api-database/src/main/java/org/eclipse/dirigible/components/api/db/DatabaseFacade.java @@ -15,6 +15,8 @@ import org.eclipse.dirigible.components.data.management.helpers.DatabaseResultSetHelper; import org.eclipse.dirigible.components.data.management.service.DatabaseDefinitionService; import org.eclipse.dirigible.components.data.sources.manager.DataSourcesManager; +import org.eclipse.dirigible.components.database.DirigibleConnection; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.eclipse.dirigible.components.database.NamedParameterStatement; import org.eclipse.dirigible.database.persistence.processors.identity.PersistenceNextValueIdentityProcessor; import org.eclipse.dirigible.database.sql.SqlFactory; @@ -109,7 +111,7 @@ public DatabaseDefinitionService getDatabaseDefinitionService() { * * @return the default data source */ - public static final DataSource getDefaultDataSource() { + public static final DirigibleDataSource getDefaultDataSource() { return DatabaseFacade.get() .getDataSourcesManager() .getDefaultDataSource(); @@ -146,7 +148,7 @@ public static final String getMetadata(String datasourceName) throws SQLExceptio * @param datasourceName the datasource name * @return the data source */ - private static DataSource getDataSource(String datasourceName) { + private static DirigibleDataSource getDataSource(String datasourceName) { return datasourceName == null || "".equals(datasourceName.trim()) || "DefaultDB".equals(datasourceName) ? DatabaseFacade.get() .getDataSourcesManager() .getDefaultDataSource() @@ -521,7 +523,7 @@ public static final int updateNamed(String sql) throws Exception { * @return the connection * @throws SQLException the SQL exception */ - public static final Connection getConnection() throws SQLException { + public static final DirigibleConnection getConnection() throws SQLException { return getConnection(null); } @@ -532,8 +534,8 @@ public static final Connection getConnection() throws SQLException { * @return the connection * @throws SQLException the SQL exception */ - public static final Connection getConnection(String datasourceName) throws SQLException { - DataSource dataSource = getDataSource(datasourceName); + public static final DirigibleConnection getConnection(String datasourceName) throws SQLException { + DirigibleDataSource dataSource = getDataSource(datasourceName); if (dataSource == null) { String error = format("DataSource {0} not known.", datasourceName); throw new IllegalArgumentException(error); diff --git a/components/api/api-modules-javascript/src/main/resources/META-INF/dirigible/modules/src/db/database.ts b/components/api/api-modules-javascript/src/main/resources/META-INF/dirigible/modules/src/db/database.ts index 651662d81df..bd80c38ba5f 100644 --- a/components/api/api-modules-javascript/src/main/resources/META-INF/dirigible/modules/src/db/database.ts +++ b/components/api/api-modules-javascript/src/main/resources/META-INF/dirigible/modules/src/db/database.ts @@ -84,98 +84,139 @@ export function getProductName(datasourceName) { return productName; }; -export function getConnection(datasourceName) { - const connection = new Connection(); +export function getConnection(datasourceName: string | undefined) : Connection { var native; if (datasourceName) { native = DatabaseFacade.getConnection(datasourceName); } else { native = DatabaseFacade.getConnection(); } - connection.native = native; - return connection; + return new Connection(native); }; +export enum DatabaseSystem { + UNKNOWN, DERBY, POSTGRESQL, H2, MARIADB, HANA, SNOWFLAKE, MYSQL, MONGODB, SYBASE +} + /** * Connection object */ -function Connection() { +export class Connection { + + private readonly native; + + constructor(native: any) { + this.native = native; + } + + public isOfType(databaseSystem: DatabaseSystem): boolean { + const actualDatabaseSystem = this.getDatabaseSystem(); + return actualDatabaseSystem == databaseSystem; + } - this.prepareStatement = function (sql) { + public getDatabaseSystem() { + const dbSystem = this.native.getDatabaseSystem().name(); + switch (dbSystem) { + case "DERBY": + return DatabaseSystem.DERBY; + case "POSTGRESQL": + return DatabaseSystem.POSTGRESQL; + case "H2": + return DatabaseSystem.H2; + case "MARIADB": + return DatabaseSystem.MARIADB; + case "HANA": + return DatabaseSystem.HANA; + case "SNOWFLAKE": + return DatabaseSystem.SNOWFLAKE; + case "MYSQL": + return DatabaseSystem.MYSQL; + case "MONGODB": + return DatabaseSystem.MONGODB; + case "SYBASE": + return DatabaseSystem.SYBASE; + case "UNKNOWN": + return DatabaseSystem.UNKNOWN; + default: + throw Error("Missing mapping for database system type " + dbSystem); + } + } + + public prepareStatement(sql) { const preparedStatement = new PreparedStatement(); const native = this.native.prepareStatement(sql); preparedStatement.native = native; return preparedStatement; - }; + } - this.prepareCall = function (sql) { + public prepareCall(sql) { const callableStatement = new CallableStatement(); const native = this.native.prepareCall(sql); callableStatement.native = native; return callableStatement; - }; + } - this.close = function () { + public close() { if (!this.isClosed()) { this.native.close(); } - }; + } - this.commit = function () { + public commit() { this.native.commit(); - }; + } - this.getAutoCommit = function () { + public getAutoCommit() { return this.native.getAutoCommit(); - }; + } - this.getCatalog = function () { + public getCatalog() { return this.native.getCatalog(); - }; + } - this.getSchema = function () { + public getSchema() { return this.native.getSchema(); - }; + } - this.getTransactionIsolation = function () { + public getTransactionIsolation() { return this.native.getTransactionIsolation(); - }; + } - this.isClosed = function () { + public isClosed() { return this.native.isClosed(); - }; + } - this.isReadOnly = function () { + public isReadOnly() { return this.native.isReadOnly(); - }; + } - this.isValid = function () { + public isValid() { return this.native.isValid(); - }; + } - this.rollback = function () { + public rollback() { return this.native.rollback(); - }; + } - this.setAutoCommit = function (autoCommit) { + public setAutoCommit(autoCommit) { this.native.setAutoCommit(autoCommit); - }; + } - this.setCatalog = function (catalog) { + public setCatalog(catalog) { this.native.setCatalog(catalog); - }; + } - this.setReadOnly = function (readOnly) { + public setReadOnly(readOnly) { this.native.setReadOnly(readOnly); - }; + } - this.setSchema = function (schema) { + public setSchema(schema) { this.native.setSchema(schema); - }; + } - this.setTransactionIsolation = function (transactionIsolation) { + public setTransactionIsolation(transactionIsolation) { this.native.setTransactionIsolation(transactionIsolation); - }; + } } /** diff --git a/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/ConnectionEnhancer.java b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/ConnectionEnhancer.java new file mode 100644 index 00000000000..2ff7159fa3a --- /dev/null +++ b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/ConnectionEnhancer.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Eclipse Dirigible contributors + * + * All rights reserved. This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.dirigible.components.database; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface ConnectionEnhancer { + + boolean isApplicable(DatabaseSystem databaseSystem); + + void apply(Connection connection) throws SQLException; +} diff --git a/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseConfigurator.java b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseConfigurator.java new file mode 100644 index 00000000000..8b159d4c5ed --- /dev/null +++ b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseConfigurator.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Eclipse Dirigible contributors + * + * All rights reserved. This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.dirigible.components.database; + +import com.zaxxer.hikari.HikariConfig; + +/** + * The Interface DatabaseConfigurator. + */ +public interface DatabaseConfigurator { + + boolean isApplicable(DatabaseSystem databaseSystem); + + void apply(HikariConfig config); +} diff --git a/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystem.java b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystem.java new file mode 100644 index 00000000000..ab166d69695 --- /dev/null +++ b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystem.java @@ -0,0 +1,48 @@ +package org.eclipse.dirigible.components.database; + +public enum DatabaseSystem { + // when adding or changing enum values do NOT forget to + // update the JavaScript API in the connection class located at: + // dirigible/modules/src/db/database.ts + UNKNOWN, DERBY, POSTGRESQL, H2, MARIADB, HANA, SNOWFLAKE, MYSQL, MONGODB, SYBASE; + + public boolean isH2() { + return isOfType(H2); + } + + public boolean isSnowflake() { + return isOfType(SNOWFLAKE); + } + + public boolean isHANA() { + return isOfType(HANA); + } + + public boolean isPostgreSQL() { + return isOfType(POSTGRESQL); + } + + public boolean isMariaDB() { + return isOfType(MARIADB); + } + + public boolean isMySQL() { + return isOfType(MYSQL); + } + + public boolean isUnknown() { + return isOfType(UNKNOWN); + } + + public boolean isMongoDB() { + return isOfType(MONGODB); + } + + public boolean isDerby() { + return isOfType(DERBY); + } + + public boolean isOfType(DatabaseSystem databaseSystem) { + return this == databaseSystem; + } +} diff --git a/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystemAware.java b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystemAware.java new file mode 100644 index 00000000000..00945149a8b --- /dev/null +++ b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystemAware.java @@ -0,0 +1,9 @@ +package org.eclipse.dirigible.components.database; + +public interface DatabaseSystemAware { + + DatabaseSystem getDatabaseSystem(); + + boolean isOfType(DatabaseSystem databaseSystem); + +} diff --git a/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystemDeterminer.java b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystemDeterminer.java new file mode 100644 index 00000000000..7d224006297 --- /dev/null +++ b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DatabaseSystemDeterminer.java @@ -0,0 +1,114 @@ +package org.eclipse.dirigible.components.database; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class DatabaseSystemDeterminer { + + private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseSystemDeterminer.class); + + private static final Map DB_URL_PREFIXES = Map.of(// + DatabaseSystem.H2, "jdbc:h2", // + DatabaseSystem.POSTGRESQL, "jdbc:postgresql", // + DatabaseSystem.HANA, "jdbc:sap", // + DatabaseSystem.SNOWFLAKE, "jdbc:snowflake", // + DatabaseSystem.MARIADB, "jdbc:mariadb", // + DatabaseSystem.MYSQL, "jdbc:mysql", // + DatabaseSystem.MONGODB, "jdbc:mongodb", // + DatabaseSystem.SYBASE, "jdbc:sybase", // + DatabaseSystem.DERBY, "jdbc:derby"// + ); + + private static final Map> DB_DRIVERS = Map.of(// + DatabaseSystem.H2, List.of("org.h2.Driver"), // + DatabaseSystem.POSTGRESQL, List.of("org.postgresql.Driver"), // + DatabaseSystem.HANA, List.of("com.sap.db.jdbc.Driver"), // + DatabaseSystem.SNOWFLAKE, List.of("net.snowflake.client.jdbc.SnowflakeDriver"), // + DatabaseSystem.MARIADB, List.of("org.mariadb.jdbc.Driver"), // + DatabaseSystem.MYSQL, List.of("com.mysql.cj.jdbc.Driver"), // + DatabaseSystem.MONGODB, List.of("com.mongodb.jdbc.MongoDriver"), // + DatabaseSystem.SYBASE, List.of("com.sybase.jdbc4.jdbc.SybDriver"), // + DatabaseSystem.DERBY, List.of("org.apache.derby.jdbc.ClientDriver", "org.apache.derby.jdbc.EmbeddedDriver")// + ); + + public static DatabaseSystem determine(javax.sql.DataSource dataSource) throws SQLException { + if (dataSource instanceof DirigibleDataSource ddc) { + return ddc.getDatabaseSystem(); + } + + try (Connection connection = dataSource.getConnection()) { + return determine(connection); + } + } + + public static DatabaseSystem determine(Connection connection) throws SQLException { + if (connection instanceof DirigibleConnection dc) { + return dc.getDatabaseSystem(); + } + + DatabaseMetaData metaData = connection.getMetaData(); + String jdbcUrl = metaData.getURL(); + String driverClass = metaData.getDriverName(); + + return determine(jdbcUrl, driverClass); + } + + public static DatabaseSystem determine(String jdbcUrl, String driverClass) { + DatabaseSystem databaseSystem = determineSystemByJdbcUrl(jdbcUrl)// + .orElseGet(() -> determineSystemByDriverClass(driverClass)); + if (databaseSystem.isUnknown()) { + LOGGER.warn("JDBC url [{}] and driver [{}] are determined as [{}]. Most probably something is misconfigured", jdbcUrl, + driverClass, databaseSystem); + + } + LOGGER.debug("JDBC url [{}] and driver [{}] are determined as [{}]", jdbcUrl, driverClass, databaseSystem); + return databaseSystem; + } + + private static Optional determineSystemByJdbcUrl(String jdbcUrl) { + return DB_URL_PREFIXES.entrySet() + .stream() + .filter(entry -> isJdbcUrlStartWithString(jdbcUrl, entry.getValue())) + .findFirst() + .map(Map.Entry::getKey); + } + + private static DatabaseSystem determineSystemByDriverClass(String driverClass) { + return DB_DRIVERS.entrySet() + .stream() + .filter(entry -> usedOneOfDrivers(driverClass, entry.getValue())) + .findFirst() + .map(Map.Entry::getKey) + .orElse(DatabaseSystem.UNKNOWN); + } + + private static boolean isJdbcUrlStartWithString(String jdbcUrl, String prefix) { + if (StringUtils.isBlank(jdbcUrl)) { + LOGGER.warn("Received blank JDBC URL [{}]", jdbcUrl, jdbcUrl); + return false; + } + return jdbcUrl.trim() + .toLowerCase() + .startsWith(prefix); + } + + private static boolean usedOneOfDrivers(String driverClass, List drivers) { + String trimmedDriverClassName = safelyTrim(driverClass); + return drivers.stream() + .anyMatch(driver -> Objects.equals(trimmedDriverClassName, safelyTrim(driver))); + } + + private static String safelyTrim(String string) { + return null != string ? string.trim() : null; + } + +} diff --git a/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DirigibleConnection.java b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DirigibleConnection.java new file mode 100644 index 00000000000..0a82570e87d --- /dev/null +++ b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DirigibleConnection.java @@ -0,0 +1,7 @@ +package org.eclipse.dirigible.components.database; + +import java.sql.Connection; + +public interface DirigibleConnection extends Connection, DatabaseSystemAware { + +} diff --git a/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DirigibleDataSource.java b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DirigibleDataSource.java new file mode 100644 index 00000000000..f05e6c1c407 --- /dev/null +++ b/components/core/core-database/src/main/java/org/eclipse/dirigible/components/database/DirigibleDataSource.java @@ -0,0 +1,16 @@ +package org.eclipse.dirigible.components.database; + +import javax.sql.DataSource; +import java.sql.SQLException; + +public interface DirigibleDataSource extends DataSource, DatabaseSystemAware { + + void close(); + + @Override + DirigibleConnection getConnection() throws SQLException; + + @Override + DirigibleConnection getConnection(String username, String password) throws SQLException; + +} diff --git a/components/core/core-database/src/test/java/org/eclipse/dirigible/components/database/DatabaseSystemDeterminerTest.java b/components/core/core-database/src/test/java/org/eclipse/dirigible/components/database/DatabaseSystemDeterminerTest.java new file mode 100644 index 00000000000..2beafc76502 --- /dev/null +++ b/components/core/core-database/src/test/java/org/eclipse/dirigible/components/database/DatabaseSystemDeterminerTest.java @@ -0,0 +1,133 @@ +package org.eclipse.dirigible.components.database; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(MockitoExtension.class) +class DatabaseSystemDeterminerTest { + + @InjectMocks + private DatabaseSystemDeterminer databaseSystemDeterminer; + + @Test + void testDetermineDerby_withUrl() { + testDetermine_withUrl("jdbc:derby://localhost:1527/databaseName;create=true", DatabaseSystem.DERBY); + } + + @Test + void testDetermineDerby_withDriverUrl() { + testDetermine_withDriver("org.apache.derby.jdbc.EmbeddedDriver", DatabaseSystem.DERBY); + } + + @Test + void testDetermineDerby_withDriverUrl2() { + testDetermine_withDriver("org.apache.derby.jdbc.ClientDriver", DatabaseSystem.DERBY); + } + + @Test + void testDetermineH2_withUrl() { + testDetermine_withUrl("jdbc:h2:mem:test", DatabaseSystem.H2); + } + + @Test + void testDetermineH2_withDriverUrl() { + testDetermine_withDriver("org.h2.Driver", DatabaseSystem.H2); + } + + @Test + void testDeterminePostgreSQL_withUrl() { + testDetermine_withUrl("jdbc:postgresql://localhost:5432/testdb", DatabaseSystem.POSTGRESQL); + } + + @Test + void testDeterminePostgreSQL_withDriver() { + testDetermine_withDriver("org.postgresql.Driver", DatabaseSystem.POSTGRESQL); + } + + @Test + void testDetermineHANA_withUrl() { + testDetermine_withUrl("jdbc:sap://hana-db:30015", DatabaseSystem.HANA); + } + + @Test + void testDetermineHANA_withDriver() { + testDetermine_withDriver("com.sap.db.jdbc.Driver", DatabaseSystem.HANA); + } + + @Test + void testDetermineSnowflake_withUrl() { + testDetermine_withUrl("jdbc:snowflake://account.snowflakecomputing.com", DatabaseSystem.SNOWFLAKE); + } + + @Test + void testDetermineSnowflake_withDriver() { + testDetermine_withDriver("net.snowflake.client.jdbc.SnowflakeDriver", DatabaseSystem.SNOWFLAKE); + } + + @Test + void testDetermineMariaDB_withUrl() { + testDetermine_withUrl("jdbc:mariadb://localhost:3306/testdb", DatabaseSystem.MARIADB); + } + + @Test + void testDetermineMariaDB_withDriver() { + testDetermine_withDriver("org.mariadb.jdbc.Driver", DatabaseSystem.MARIADB); + } + + @Test + void testDetermineMySQL_withUrl() { + testDetermine_withUrl("jdbc:mysql://localhost:3306/testdb", DatabaseSystem.MYSQL); + } + + @Test + void testDetermineMySQL_withDriver() { + testDetermine_withDriver("com.mysql.cj.jdbc.Driver", DatabaseSystem.MYSQL); + } + + @Test + void testDetermineMongoDB_withUrl() { + testDetermine_withUrl("jdbc:mongodb://localhost:27017/mydatabase", DatabaseSystem.MONGODB); + } + + @Test + void testDetermineMongoDB_withDriver() { + testDetermine_withDriver("com.mongodb.jdbc.MongoDriver", DatabaseSystem.MONGODB); + } + + @Test + void testDetermineSybase_withUrl() { + testDetermine_withUrl("jdbc:sybase:Tds:localhost:3306/database", DatabaseSystem.SYBASE); + } + + @Test + void testDetermineSybase_withDriver() { + testDetermine_withDriver("com.sybase.jdbc4.jdbc.SybDriver", DatabaseSystem.SYBASE); + } + + @Test + void testDetermineUnknown_withUrl() { + testDetermine_withUrl("jdbc:unknown://localhost:1234/testdb", DatabaseSystem.UNKNOWN); + } + + @Test + void testDetermineUnknown_withDriver() { + testDetermine_withDriver("com.unknown.Driver", DatabaseSystem.UNKNOWN); + } + + private void testDetermine_withUrl(String jdbcUrl, DatabaseSystem expectedType) { + DatabaseSystem result = DatabaseSystemDeterminer.determine(jdbcUrl, null); + + assertEquals(expectedType, result); + } + + private void testDetermine_withDriver(String driverClass, DatabaseSystem expectedType) { + DatabaseSystem result = DatabaseSystemDeterminer.determine(null, driverClass); + + assertEquals(expectedType, result); + } + +} diff --git a/components/data/data-export/src/main/java/org/eclipse/dirigible/components/data/export/service/DataExportService.java b/components/data/data-export/src/main/java/org/eclipse/dirigible/components/data/export/service/DataExportService.java index 265c43b0c58..8536db7efe9 100644 --- a/components/data/data-export/src/main/java/org/eclipse/dirigible/components/data/export/service/DataExportService.java +++ b/components/data/data-export/src/main/java/org/eclipse/dirigible/components/data/export/service/DataExportService.java @@ -9,24 +9,14 @@ */ package org.eclipse.dirigible.components.data.export.service; -import static java.text.MessageFormat.format; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.apache.commons.io.output.WriterOutputStream; import org.eclipse.dirigible.commons.api.helpers.GsonHelper; import org.eclipse.dirigible.components.api.platform.WorkspaceFacade; import org.eclipse.dirigible.components.base.helpers.JsonHelper; import org.eclipse.dirigible.components.data.csvim.domain.CsvFile; -import org.eclipse.dirigible.components.data.management.domain.DatabaseMetadata; import org.eclipse.dirigible.components.data.management.helpers.DatabaseMetadataHelper; import org.eclipse.dirigible.components.data.management.load.DataSourceMetadataLoader; import org.eclipse.dirigible.components.data.management.service.DatabaseDefinitionService; @@ -35,6 +25,7 @@ import org.eclipse.dirigible.components.data.structures.domain.Table; import org.eclipse.dirigible.components.data.structures.domain.TableColumn; import org.eclipse.dirigible.components.data.transfer.service.DataTransferSchemaTopologyService; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.eclipse.dirigible.components.ide.workspace.domain.File; import org.eclipse.dirigible.components.ide.workspace.domain.Project; import org.eclipse.dirigible.components.ide.workspace.domain.Workspace; @@ -46,9 +37,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static java.text.MessageFormat.format; /** * The Class DataExportService. @@ -126,7 +124,7 @@ public DataExportService(DataSourcesManager datasourceManager, WorkspaceService */ public void exportSchemaInCsvs(String datasource, String schema) { try { - javax.sql.DataSource dataSource = datasourceManager.getDataSource(datasource); + DirigibleDataSource dataSource = datasourceManager.getDataSource(datasource); if (dataSource != null) { Workspace workspace; ArrayList csvFiles = new ArrayList<>(); @@ -161,8 +159,8 @@ public void exportSchemaInCsvs(String datasource, String schema) { String artifact = table.get("name") .getAsString(); String sql = "SELECT * FROM \"" + schema + "\".\"" + artifact + "\""; - try (Connection connection = dataSource.getConnection()) { - sql = SqlDialectFactory.getDialect(connection) + try { + sql = SqlDialectFactory.getDialect(dataSource) .allQuery("\"" + schema + "\".\"" + artifact + "\""); } catch (Exception e) { logger.error(e.getMessage(), e); diff --git a/components/data/data-export/src/main/java/org/eclipse/dirigible/components/data/export/service/DatabaseExportService.java b/components/data/data-export/src/main/java/org/eclipse/dirigible/components/data/export/service/DatabaseExportService.java index 72cbec125b2..c563e67491e 100644 --- a/components/data/data-export/src/main/java/org/eclipse/dirigible/components/data/export/service/DatabaseExportService.java +++ b/components/data/data-export/src/main/java/org/eclipse/dirigible/components/data/export/service/DatabaseExportService.java @@ -9,18 +9,14 @@ */ package org.eclipse.dirigible.components.data.export.service; - -import java.io.IOException; -import java.io.OutputStream; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.eclipse.dirigible.commons.api.helpers.GsonHelper; import org.eclipse.dirigible.components.data.management.helpers.DatabaseMetadataHelper; import org.eclipse.dirigible.components.data.management.service.DatabaseExecutionService; import org.eclipse.dirigible.components.data.sources.manager.DataSourcesManager; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.dialects.SqlDialectFactory; import org.slf4j.Logger; @@ -28,9 +24,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; +import java.io.IOException; +import java.io.OutputStream; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * The Class DataSourceMetadataService. @@ -72,17 +71,15 @@ public DatabaseExportService(DataSourcesManager datasourceManager, DatabaseExecu * @param output the output */ public void exportStructure(String datasource, String schema, String structure, OutputStream output) { - javax.sql.DataSource dataSource = datasourceManager.getDataSource(datasource); + DirigibleDataSource dataSource = datasourceManager.getDataSource(datasource); if (dataSource != null) { String structureName = "\"" + schema + "\".\"" + structure + "\""; String sql = "SELECT * FROM " + structureName; - String productName = null; try (Connection connection = dataSource.getConnection()) { - ISqlDialect dialect = SqlDialectFactory.getDialect(connection); + ISqlDialect dialect = SqlDialectFactory.getDialect(dataSource); sql = dialect.allQuery(structureName); - productName = connection.getMetaData() - .getDatabaseProductName(); - if ("MongoDB".equals(productName)) { + if (dataSource.getDatabaseSystem() + .isMongoDB()) { dialect.exportData(connection, structure, output); return; } @@ -103,13 +100,12 @@ public void exportStructure(String datasource, String schema, String structure, * @return the string */ public String structureExportType(String datasource, String schema, String structure) { - javax.sql.DataSource dataSource = datasourceManager.getDataSource(datasource); + DirigibleDataSource dataSource = datasourceManager.getDataSource(datasource); if (dataSource != null) { String productName = null; - try (Connection connection = dataSource.getConnection()) { - productName = connection.getMetaData() - .getDatabaseProductName(); - if ("MongoDB".equals(productName)) { + try { + if (dataSource.getDatabaseSystem() + .isMongoDB()) { return "json"; } } catch (Exception e) { @@ -134,7 +130,7 @@ public void exportSchema(String datasource, String schema, OutputStream output) try { zipOutputStream = new ZipOutputStream(output); - javax.sql.DataSource dataSource = datasourceManager.getDataSource(datasource); + DirigibleDataSource dataSource = datasourceManager.getDataSource(datasource); if (dataSource != null) { String metadata = DatabaseMetadataHelper.getMetadataAsJson(dataSource); JsonElement database = GsonHelper.parseJson(metadata); @@ -158,8 +154,8 @@ public void exportSchema(String datasource, String schema, OutputStream output) .getAsString(); String artifactName = "\"" + schema + "\".\"" + artifact + "\""; String sql = "SELECT * FROM " + artifactName; - try (Connection connection = dataSource.getConnection()) { - sql = SqlDialectFactory.getDialect(connection) + try { + sql = SqlDialectFactory.getDialect(dataSource) .allQuery(artifactName); } catch (Exception e) { logger.error(e.getMessage(), e); diff --git a/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/service/DataImportService.java b/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/service/DataImportService.java index d73dcf7e6ed..677ea5258f2 100644 --- a/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/service/DataImportService.java +++ b/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/service/DataImportService.java @@ -9,18 +9,19 @@ */ package org.eclipse.dirigible.components.data.export.service; -import java.io.IOException; -import java.io.InputStream; -import java.sql.Connection; -import javax.sql.DataSource; import org.eclipse.dirigible.components.data.csvim.domain.CsvFile; import org.eclipse.dirigible.components.data.csvim.processor.CsvimProcessor; import org.eclipse.dirigible.components.data.sources.manager.DataSourcesManager; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.dialects.SqlDialectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; + /** * The Class DataImportService. */ @@ -56,12 +57,11 @@ public class DataImportService { public void importData(String datasource, String schema, String table, Boolean header, Boolean useHeaderNames, String delimField, String delimEnclosing, String sequence, Boolean distinguishEmptyFromNull, InputStream is) throws IOException, Exception { - DataSource dataSource = datasourceManager.getDataSource(datasource); + DirigibleDataSource dataSource = datasourceManager.getDataSource(datasource); try (Connection connection = dataSource.getConnection()) { - ISqlDialect dialect = SqlDialectFactory.getDialect(connection); - String productName = connection.getMetaData() - .getDatabaseProductName(); - if ("MongoDB".equals(productName)) { + ISqlDialect dialect = SqlDialectFactory.getDialect(dataSource); + if (dataSource.getDatabaseSystem() + .isMongoDB()) { dialect.importData(connection, table, is); return; } diff --git a/components/data/data-management/src/main/java/org/eclipse/dirigible/components/data/management/helpers/DatabaseMetadataHelper.java b/components/data/data-management/src/main/java/org/eclipse/dirigible/components/data/management/helpers/DatabaseMetadataHelper.java index ebb2a54ec01..92ff897e782 100644 --- a/components/data/data-management/src/main/java/org/eclipse/dirigible/components/data/management/helpers/DatabaseMetadataHelper.java +++ b/components/data/data-management/src/main/java/org/eclipse/dirigible/components/data/management/helpers/DatabaseMetadataHelper.java @@ -9,16 +9,12 @@ */ package org.eclipse.dirigible.components.data.management.helpers; -import java.sql.*; -import java.util.ArrayList; -import java.util.List; - -import javax.sql.DataSource; - import org.eclipse.dirigible.commons.api.helpers.GsonHelper; import org.eclipse.dirigible.components.data.management.domain.*; -import org.eclipse.dirigible.components.database.DatabaseParameters; import org.eclipse.dirigible.components.database.DatabaseNameNormalizer; +import org.eclipse.dirigible.components.database.DatabaseParameters; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.eclipse.dirigible.components.database.DatabaseSystemDeterminer; import org.eclipse.dirigible.database.sql.DatabaseType; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlKeywords; @@ -26,6 +22,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.sql.DataSource; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + /** * The Database Metadata Helper. */ @@ -291,17 +292,13 @@ public static List listSequences(Connection connection, String String query = null; - if (dmd.getDatabaseProductName() - .equals("MariaDB") - || dmd.getDatabaseProductName() - .equals("MySQL")) { + DatabaseSystem databaseSystem = DatabaseSystemDeterminer.determine(connection); + if (databaseSystem.isMariaDB() || databaseSystem.isMySQL()) { query = String.format( - "SELECT column_name FROM information_schema.columns WHERE table_schema = \'%s\' AND extra = 'auto_increment'", name); - } else if (dmd.getDatabaseProductName() - .equals("Snowflake")) { + "SELECT column_name FROM information_schema.columns WHERE table_schema = '%s' AND extra = 'auto_increment'", name); + } else if (databaseSystem.isSnowflake()) { query = "SHOW SEQUENCES"; - } else if (!dmd.getDatabaseProductName() - .equals("MongoDB")) { + } else if (!databaseSystem.isMongoDB()) { query = "SELECT * FROM information_schema.sequences"; } @@ -310,13 +307,9 @@ public static List listSequences(Connection connection, String try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { String sequenceName; - if (dmd.getDatabaseProductName() - .equals("MariaDB") - || dmd.getDatabaseProductName() - .equals("MySQL")) { + if (databaseSystem.isMariaDB() || databaseSystem.isMySQL()) { sequenceName = resultSet.getString("column_name"); - } else if (dmd.getDatabaseProductName() - .equals("Snowflake")) { + } else if (databaseSystem.isSnowflake()) { sequenceName = resultSet.getString("name"); } else { sequenceName = resultSet.getString("sequence_name"); @@ -332,7 +325,6 @@ public static List listSequences(Connection connection, String return result; } - /** * Describe table. * @@ -465,6 +457,7 @@ public interface ColumnsIteratorCallback { void onColumn(String name, String type, String size, boolean isNullable, boolean isKey, int scale); } + /** * The Interface ProceduresColumnsIteratorCallback. */ @@ -487,6 +480,7 @@ void onProcedureColumn(String name, int kind, String type, int precision, int le String remarks); } + /** * The Interface FunctionColumnsIteratorCallback. */ @@ -509,6 +503,7 @@ void onFunctionColumn(String name, int kind, String type, int precision, int len String remarks); } + /** * The Interface IndicesIteratorCallback. */ @@ -532,6 +527,7 @@ void onIndex(String indexName, String indexType, String columnName, boolean isNo String ordinalPosition, String sortOrder, String cardinality, String pagesIndex, String filterCondition); } + /** * The Interface IndicesIteratorCallback. */ @@ -640,7 +636,6 @@ public static void iterateProcedureDefinition(Connection connection, String cata try { - while (columns.next()) { if (procedureColumnsIteratorCallback != null) { String cname = columns.getString(COLUMN_NAME); @@ -677,7 +672,6 @@ public static void iterateFunctionDefinition(Connection connection, String catal try { - while (columns.next()) { if (functionColumnsIteratorCallback != null) { String cname = columns.getString(COLUMN_NAME); diff --git a/components/data/data-management/src/main/java/org/eclipse/dirigible/components/data/management/load/DataSourceMetadataLoader.java b/components/data/data-management/src/main/java/org/eclipse/dirigible/components/data/management/load/DataSourceMetadataLoader.java index 6648ef86603..297df220552 100644 --- a/components/data/data-management/src/main/java/org/eclipse/dirigible/components/data/management/load/DataSourceMetadataLoader.java +++ b/components/data/data-management/src/main/java/org/eclipse/dirigible/components/data/management/load/DataSourceMetadataLoader.java @@ -9,36 +9,25 @@ */ package org.eclipse.dirigible.components.data.management.load; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.sql.DataSource; - +import com.google.common.base.CaseFormat; import org.eclipse.dirigible.commons.config.Configuration; -import org.eclipse.dirigible.components.data.management.domain.DatabaseMetadata; import org.eclipse.dirigible.components.data.sources.manager.DataSourcesManager; -import org.eclipse.dirigible.components.data.structures.domain.Table; -import org.eclipse.dirigible.components.data.structures.domain.TableColumn; -import org.eclipse.dirigible.components.data.structures.domain.TableConstraint; -import org.eclipse.dirigible.components.data.structures.domain.TableConstraintCheck; -import org.eclipse.dirigible.components.data.structures.domain.TableConstraintForeignKey; -import org.eclipse.dirigible.components.data.structures.domain.TableConstraintUnique; +import org.eclipse.dirigible.components.data.structures.domain.*; import org.eclipse.dirigible.components.database.DatabaseParameters; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.eclipse.dirigible.components.database.DatabaseSystemDeterminer; import org.eclipse.dirigible.database.sql.ISqlKeywords; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import com.google.common.base.CaseFormat; - +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; /** * The Class DataSourceMetadataLoader. @@ -188,7 +177,6 @@ public static void addPrimaryKeys(DatabaseMetaData databaseMetadata, Connection primaryKeys = databaseMetadata.getPrimaryKeys(connection.getCatalog(), schema, normalizeTableName(tableMetadata.getName() .toLowerCase())); if (!primaryKeys.next()) { - return; } else { iteratePrimaryKeys(tableMetadata, primaryKeys); } @@ -244,7 +232,6 @@ public static void addForeignKeys(DatabaseMetaData databaseMetadata, Connection foreignKeys = databaseMetadata.getImportedKeys(connection.getCatalog(), schema, normalizeTableName(tableMetadata.getName() .toLowerCase())); if (!foreignKeys.next()) { - return; } else { iterateForeignKeys(tableMetadata, foreignKeys); } @@ -267,8 +254,6 @@ private static void iterateForeignKeys(Table tableMetadata, ResultSet foreignKey } while (foreignKeys.next()); } - - /** * Add indices. * @@ -410,12 +395,10 @@ public static String getTableSchema(DataSource dataSource, String tableName) thr public static List getTablesInSchema(DataSource dataSource, String schemaName) throws SQLException { List tableNames = new ArrayList(); try (Connection connection = dataSource.getConnection()) { + DatabaseSystem databaseSystem = DatabaseSystemDeterminer.determine(dataSource); DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet schemas; - if (!(databaseMetaData.getDatabaseProductName() - .equals("MariaDB") - || databaseMetaData.getDatabaseProductName() - .equals("MySQL"))) { + if (!(databaseSystem.isMariaDB() || databaseSystem.isMySQL())) { schemas = databaseMetaData.getSchemas(null, schemaName); } else { schemas = databaseMetaData.getCatalogs(); diff --git a/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/DataSourceInitializerContributorSnowpark.java b/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/DataSourceInitializerContributorSnowpark.java deleted file mode 100644 index f2c68257ece..00000000000 --- a/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/DataSourceInitializerContributorSnowpark.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2024 Eclipse Dirigible contributors - * - * All rights reserved. This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v20.html - * - * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.dirigible.components.data.source.snowpark; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.dirigible.commons.config.Configuration; -import org.eclipse.dirigible.components.data.sources.domain.DataSource; -import org.eclipse.dirigible.components.data.sources.manager.DataSourceInitializerContributor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Properties; - -/** - * The Class DataSourceInitializerContributorSnowpark. - */ -@org.springframework.context.annotation.Configuration -public class DataSourceInitializerContributorSnowpark implements DataSourceInitializerContributor { - /** The Constant logger. */ - private static final Logger logger = LoggerFactory.getLogger(DataSourceInitializerContributorSnowpark.class); - public static final String TOKEN_FILE_PATH = "/snowflake/session/token"; - - /** - * Contribute. - * - * @param dataSource the data source - * @param properties the properties - */ - @Override - public void contribute(DataSource dataSource, Properties properties) { - if (!dataSource.getName() - .startsWith("SNOWFLAKE")) { - logger.info("[{}] will NOT contribute to datasource [{}]", this, dataSource.getName()); - return; - } - try { - String url; - properties.put("dataSource.CLIENT_SESSION_KEEP_ALIVE", true); - setPropertyIfConfigAvailable("SNOWFLAKE_ACCOUNT", "dataSource.account", properties); - setPropertyIfConfigAvailable("SNOWFLAKE_WAREHOUSE", "dataSource.warehouse", properties); - setPropertyIfConfigAvailable("SNOWFLAKE_DATABASE", "dataSource.db", properties); - setPropertyIfConfigAvailable("SNOWFLAKE_SCHEMA", "dataSource.schema", properties); - - if (hasTokenFile()) { - logger.info("There IS token file. OAuth will be used for [{}]", dataSource.getName()); - - properties.put("dataSource.authenticator", "OAUTH"); - properties.put("dataSource.token", new String(Files.readAllBytes(Paths.get(TOKEN_FILE_PATH)))); - properties.put("dataSource.insecureMode", true); - url = "jdbc:snowflake://" + Configuration.get("SNOWFLAKE_HOST") + ":" + Configuration.get("SNOWFLAKE_PORT"); - logger.info("Built url [{}]", url); - } else { - logger.info("There is NO token file. User/password will be used for [{}]", dataSource.getName()); - - setPropertyIfConfigAvailable("SNOWFLAKE_ROLE", "dataSource.role", properties); - setPropertyIfConfigAvailable("SNOWFLAKE_USERNAME", "dataSource.user", properties); - setPropertyIfConfigAvailable("SNOWFLAKE_PASSWORD", "dataSource.password", properties); - - url = Configuration.get("SNOWFLAKE_URL", dataSource.getUrl()); - } - - properties.put("jdbcUrl", url); - properties.put("dataSource.url", url); - - } catch (IOException ex) { - logger.error("Invalid configuration for the datasource: [{}]", dataSource.getName(), ex); - } - } - - private static boolean hasTokenFile() { - return Files.exists(Paths.get(TOKEN_FILE_PATH)); - } - - private void setPropertyIfConfigAvailable(String configName, String propertyName, Properties properties) { - String value = Configuration.get(configName); - if (StringUtils.isNotBlank(value)) { - logger.debug("Setting property [{}] from config [{}]", propertyName, configName); - properties.put(propertyName, value); - } else { - logger.debug("Will NOT set property [{}] since config [{}] value is [{}]", propertyName, configName, value); - } - } -} diff --git a/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/SnowflakeConnectionEnhancer.java b/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/SnowflakeConnectionEnhancer.java new file mode 100644 index 00000000000..4a88d6fdbf5 --- /dev/null +++ b/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/SnowflakeConnectionEnhancer.java @@ -0,0 +1,22 @@ +package org.eclipse.dirigible.components.data.source.snowpark; + +import org.eclipse.dirigible.components.database.ConnectionEnhancer; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.springframework.stereotype.Component; + +import java.sql.Connection; +import java.sql.SQLException; + +@Component +class SnowflakeConnectionEnhancer implements ConnectionEnhancer { + @Override + public boolean isApplicable(DatabaseSystem databaseSystem) { + return databaseSystem.isSnowflake(); + } + + @Override + public void apply(Connection connection) throws SQLException { + connection.createStatement() + .executeQuery("ALTER SESSION SET JDBC_QUERY_RESULT_FORMAT='JSON'"); + } +} diff --git a/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/SnowflakeDatabaseConfigurator.java b/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/SnowflakeDatabaseConfigurator.java new file mode 100644 index 00000000000..68b621dfddc --- /dev/null +++ b/components/data/data-source-snowpark/src/main/java/org/eclipse/dirigible/components/data/source/snowpark/SnowflakeDatabaseConfigurator.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 Eclipse Dirigible contributors + * + * All rights reserved. This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.dirigible.components.data.source.snowpark; + +import com.zaxxer.hikari.HikariConfig; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.dirigible.commons.config.Configuration; +import org.eclipse.dirigible.components.database.DatabaseConfigurator; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + +@Component +public class SnowflakeDatabaseConfigurator implements DatabaseConfigurator { + + /** The Constant logger. */ + private static final Logger logger = LoggerFactory.getLogger(SnowflakeDatabaseConfigurator.class); + public static final String TOKEN_FILE_PATH = "/snowflake/session/token"; + + @Override + public boolean isApplicable(DatabaseSystem databaseSystem) { + return databaseSystem.isSnowflake(); + } + + @Override + public void apply(HikariConfig config) { + config.setConnectionTestQuery("SELECT 1"); // connection validation query + config.setKeepaliveTime(TimeUnit.MINUTES.toMillis(5)); // validation execution interval, must be bigger than idle timeout + config.setMaxLifetime(TimeUnit.MINUTES.toMillis(9)); // recreate connections after specified time + + config.addDataSourceProperty("CLIENT_SESSION_KEEP_ALIVE", true); + config.addDataSourceProperty("CLIENT_SESSION_KEEP_ALIVE_HEARTBEAT_FREQUENCY", 900); + + addDataSourcePropertyIfConfigAvailable("SNOWFLAKE_WAREHOUSE", "warehouse", config); + addDataSourcePropertyIfConfigAvailable("SNOWFLAKE_ACCOUNT", "account", config); + addDataSourcePropertyIfConfigAvailable("SNOWFLAKE_DATABASE", "db", config); + addDataSourcePropertyIfConfigAvailable("SNOWFLAKE_SCHEMA", "schema", config); + + String url; + if (hasTokenFile()) { + logger.info("There IS token file. OAuth will be added to [{}]", config); + + config.setUsername(null); + config.setPassword(null); + config.addDataSourceProperty("authenticator", "OAUTH"); + config.addDataSourceProperty("token", loadTokenFile()); + url = "jdbc:snowflake://" + Configuration.get("SNOWFLAKE_HOST") + ":" + Configuration.get("SNOWFLAKE_PORT"); + } else { + logger.info("There is NO token file. User/password will be added to [{}]", config); + + addDataSourcePropertyIfConfigAvailable("SNOWFLAKE_ROLE", "role", config); + addDataSourcePropertyIfConfigAvailable("SNOWFLAKE_USERNAME", "user", config); + addDataSourcePropertyIfConfigAvailable("SNOWFLAKE_PASSWORD", "password", config); + + url = Configuration.get("SNOWFLAKE_URL", config.getJdbcUrl()); + } + logger.info("Built url [{}]", url); + config.addDataSourceProperty("url", url); + config.setJdbcUrl(url); + } + + private static String loadTokenFile() { + try { + return new String(Files.readAllBytes(Paths.get(TOKEN_FILE_PATH))); + } catch (IOException ex) { + throw new IllegalStateException("Failed to load token file from path " + TOKEN_FILE_PATH, ex); + } + } + + private static boolean hasTokenFile() { + return Files.exists(Paths.get(TOKEN_FILE_PATH)); + } + + private void addDataSourcePropertyIfConfigAvailable(String configName, String propertyName, HikariConfig config) { + String value = Configuration.get(configName); + if (StringUtils.isNotBlank(value)) { + logger.debug("Setting property [{}] from config [{}]", propertyName, configName); + config.addDataSourceProperty(propertyName, value); + } else { + logger.debug("Will NOT set property [{}] since config [{}] value is [{}]", propertyName, configName, value); + } + } + +} diff --git a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourceInitializer.java b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourceInitializer.java index ef6a8b91f18..e5b54cd5f8f 100644 --- a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourceInitializer.java +++ b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourceInitializer.java @@ -10,13 +10,13 @@ package org.eclipse.dirigible.components.data.sources.manager; import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; import org.eclipse.dirigible.commons.config.Configuration; +import org.eclipse.dirigible.commons.config.DirigibleConfig; import org.eclipse.dirigible.components.data.sources.config.DefaultDataSourceName; import org.eclipse.dirigible.components.data.sources.config.SystemDataSourceName; import org.eclipse.dirigible.components.data.sources.domain.DataSource; import org.eclipse.dirigible.components.data.sources.domain.DataSourceProperty; -import org.eclipse.dirigible.components.database.DatabaseParameters; +import org.eclipse.dirigible.components.database.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -41,26 +41,30 @@ public class DataSourceInitializer { /** The Constant logger. */ private static final Logger logger = LoggerFactory.getLogger(DataSourceInitializer.class); /** The Constant DATASOURCES. */ - private static final Map DATASOURCES = Collections.synchronizedMap(new HashMap<>()); + private static final Map DATASOURCES = Collections.synchronizedMap(new HashMap<>()); /** The application context. */ private final ApplicationContext applicationContext; /** The contributors. */ - private final List contributors; + private final List databaseConfigurators; /** The tenant data source name manager. */ private final TenantDataSourceNameManager tenantDataSourceNameManager; private final String systemDataSourceName; private final String defaultDataSourceName; + private final DirigibleDataSourceFactory dataSourceFactory; + private final Timer timer; - DataSourceInitializer(ApplicationContext applicationContext, List contributors, + DataSourceInitializer(ApplicationContext applicationContext, List databaseConfigurators, TenantDataSourceNameManager tenantDataSourceNameManager, @SystemDataSourceName String systemDataSourceName, - @DefaultDataSourceName String defaultDataSourceName) { + @DefaultDataSourceName String defaultDataSourceName, DirigibleDataSourceFactory dataSourceFactory) { this.applicationContext = applicationContext; - this.contributors = contributors; + this.databaseConfigurators = databaseConfigurators; this.tenantDataSourceNameManager = tenantDataSourceNameManager; this.systemDataSourceName = systemDataSourceName; this.defaultDataSourceName = defaultDataSourceName; + this.dataSourceFactory = dataSourceFactory; + this.timer = new Timer(); } /** @@ -69,7 +73,7 @@ public class DataSourceInitializer { * @param dataSource the data source * @return the javax.sql. data source */ - public javax.sql.DataSource initialize(DataSource dataSource) { + public DirigibleDataSource initialize(DataSource dataSource) { if (isInitialized(dataSource.getName())) { return getInitializedDataSource(dataSource.getName()); } @@ -95,7 +99,7 @@ public boolean isInitialized(String dataSourceName) { * @param dataSourceName the data source name * @return the initialized data source */ - public javax.sql.DataSource getInitializedDataSource(String dataSourceName) { + public DirigibleDataSource getInitializedDataSource(String dataSourceName) { String name = tenantDataSourceNameManager.getTenantDataSourceName(dataSourceName); return DATASOURCES.get(name); } @@ -107,7 +111,10 @@ public javax.sql.DataSource getInitializedDataSource(String dataSourceName) { * @return the managed data source */ @SuppressWarnings("resource") - private ManagedDataSource initDataSource(DataSource dataSource) { + private DirigibleDataSource initDataSource(DataSource dataSource) { + + DatabaseSystem dbType = DatabaseSystemDeterminer.determine(dataSource.getUrl(), dataSource.getDriver()); + String name = dataSource.getName(); String driver = dataSource.getDriver(); String url = dataSource.getUrl(); @@ -115,43 +122,18 @@ private ManagedDataSource initDataSource(DataSource dataSource) { String password = dataSource.getPassword(); String schema = dataSource.getSchema(); - List additionalProperties = dataSource.getProperties(); - logger.info("Initializing a datasource with name: [{}]", name); - if ("org.h2.Driver".equals(driver)) { - try { - prepareRootFolder(name); - } catch (IOException ex) { - logger.error("Invalid configuration for the datasource: [{}]", name, ex); - } - } - Properties properties = new Properties(); - Properties contributed = new Properties(); - - properties.put("driverClassName", driver); - properties.put("jdbcUrl", url); - properties.put("dataSource.url", url); - properties.put("dataSource.user", username); - properties.put("dataSource.password", password); - properties.put("dataSource.logWriter", new PrintWriter(System.out)); - - contributors.forEach(contributor -> contributor.contribute(dataSource, contributed)); - - Map hikariProperties = getHikariProperties(name); - hikariProperties.forEach(properties::setProperty); - - HikariConfig config; - - if (name.startsWith("SNOWFLAKE")) { - config = new HikariConfig(contributed); - config.setDriverClassName(driver); - config.setJdbcUrl(contributed.get("jdbcUrl") - .toString()); - } else { - properties.putAll(contributed); - config = new HikariConfig(properties); - additionalProperties.forEach(dsp -> config.addDataSourceProperty(dsp.getName(), dsp.getValue())); + if (dbType.isH2()) { + prepareRootFolder(name); } + Properties hikariProperties = getHikariProperties(name); + HikariConfig config = new HikariConfig(hikariProperties); + + config.setDriverClassName(driver); + config.setJdbcUrl(url); + config.setUsername(username); + config.setPassword(password); + config.addDataSourceProperty("logWriter", new PrintWriter(System.out)); config.setSchema(schema); config.setPoolName(name); @@ -164,45 +146,33 @@ private ManagedDataSource initDataSource(DataSource dataSource) { config.setConnectionTimeout(TimeUnit.SECONDS.toMillis(15)); config.setLeakDetectionThreshold(TimeUnit.MINUTES.toMillis(1)); // log message for possible leaked connection - boolean isHANA = Objects.equals(null == driver ? null : driver.trim(), "com.sap.db.jdbc.Driver"); - if (isHANA) { - config.setConnectionTestQuery("SELECT 1 FROM DUMMY"); // connection validation query - config.setKeepaliveTime(TimeUnit.MINUTES.toMillis(5)); // validation execution interval, must be bigger than idle timeout - } + applyDbSpecificConfigurations(dbType, config); - HikariDataSource hds = new HikariDataSource(config); + List additionalProperties = dataSource.getProperties(); + addAdditionalProperties(additionalProperties, config); - ManagedDataSource managedDataSource = new ManagedDataSource(hds); - registerDataSourceBean(name, managedDataSource); + DirigibleDataSource managedDataSource = dataSourceFactory.create(config, dbType); + registerDataSourceBean(name, managedDataSource); DATASOURCES.put(name, managedDataSource); - Runtime.getRuntime() - .addShutdownHook(new Thread(hds::close)); + if (dbType.isSnowflake()) { + // schedule data source destroy periodically since the oauth token + // expires after some time and data source have to be recreated + scheduleDataSourceDestroy(name, DirigibleConfig.SNOWFLAKE_DATA_SOURCE_LIFESPAN_SECONDS.getIntValue(), TimeUnit.SECONDS); + } return managedDataSource; } - /** - * Prepare root folder. - * - * @param name the name - * @return the string - * @throws IOException Signals that an I/O exception has occurred. - */ - private String prepareRootFolder(String name) throws IOException { - String rootFolder = (Objects.equals(defaultDataSourceName, name)) ? DatabaseParameters.DIRIGIBLE_DATABASE_H2_ROOT_FOLDER_DEFAULT - : DatabaseParameters.DIRIGIBLE_DATABASE_H2_ROOT_FOLDER + name; - String h2Root = Configuration.get(rootFolder, name); - File rootFile = new File(h2Root); - File parentFile = rootFile.getCanonicalFile() - .getParentFile(); - if (!parentFile.exists()) { - if (!parentFile.mkdirs()) { - throw new IOException(format("Creation of the root folder [{0}] of the embedded H2 database failed.", h2Root)); + private void scheduleDataSourceDestroy(String name, int duration, TimeUnit unit) { + TimerTask repeatedTask = new TimerTask() { + public void run() { + removeInitializedDataSource(name); } - } - return h2Root; + }; + long delayMillis = unit.toMillis(duration); + timer.schedule(repeatedTask, delayMillis); } /** @@ -211,8 +181,8 @@ private String prepareRootFolder(String name) throws IOException { * @param databaseName the database name * @return the hikari properties */ - private Map getHikariProperties(String databaseName) { - Map properties = new HashMap<>(); + private Properties getHikariProperties(String databaseName) { + Properties properties = new Properties(); String hikariDelimiter = "_HIKARI_"; String databaseKeyPrefix = databaseName + hikariDelimiter; int hikariDelimiterLength = hikariDelimiter.length(); @@ -224,13 +194,46 @@ private Map getHikariProperties(String databaseName) { return properties; } + private void addAdditionalProperties(List additionalProperties, HikariConfig config) { + additionalProperties.forEach(additionalProp -> config.addDataSourceProperty(additionalProp.getName(), additionalProp.getValue())); + } + + private void applyDbSpecificConfigurations(DatabaseSystem dbType, HikariConfig config) { + databaseConfigurators.stream() + .filter(dc -> dc.isApplicable(dbType)) + .forEach(dc -> dc.apply(config)); + } + + /** + * Prepare root folder. + * + * @param name the name + */ + private void prepareRootFolder(String name) { + try { + String rootFolder = (Objects.equals(defaultDataSourceName, name)) ? DatabaseParameters.DIRIGIBLE_DATABASE_H2_ROOT_FOLDER_DEFAULT + : DatabaseParameters.DIRIGIBLE_DATABASE_H2_ROOT_FOLDER + name; + String h2Root = Configuration.get(rootFolder, name); + File rootFile = new File(h2Root); + File parentFile = rootFile.getCanonicalFile() + .getParentFile(); + if (!parentFile.exists()) { + if (!parentFile.mkdirs()) { + throw new IOException(format("Creation of the root folder [{0}] of the embedded H2 database failed.", h2Root)); + } + } + } catch (IOException ex) { + logger.error("Invalid configuration for the datasource: [{}]", name, ex); + } + } + /** * Register data source bean. * * @param name the name * @param dataSource the data source */ - private void registerDataSourceBean(String name, ManagedDataSource dataSource) { + private void registerDataSourceBean(String name, DirigibleDataSource dataSource) { if (Objects.equals(systemDataSourceName, name)) { return; // bean already set by org.eclipse.dirigible.components.database.DataSourceSystemConfig } @@ -251,7 +254,16 @@ private void registerDataSourceBean(String name, ManagedDataSource dataSource) { */ public void removeInitializedDataSource(String dataSourceName) { String name = tenantDataSourceNameManager.getTenantDataSourceName(dataSourceName); - DATASOURCES.remove(name); + DirigibleDataSource removedDataSource = DATASOURCES.remove(name); + logger.info("DataSource [{}] with name [{}] will be removed if exists...", removedDataSource, name); + if (null != removedDataSource) { + removedDataSource.close(); + + GenericApplicationContext genericAppContext = (GenericApplicationContext) applicationContext; + ConfigurableListableBeanFactory beanFactory = genericAppContext.getBeanFactory(); + beanFactory.destroyBean(name); + logger.info("DataSource [{}] with name [{}] was removed", removedDataSource, name); + } } } diff --git a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourceInitializerContributor.java b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourceInitializerContributor.java deleted file mode 100644 index 7be2103e29f..00000000000 --- a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourceInitializerContributor.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2024 Eclipse Dirigible contributors - * - * All rights reserved. This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v20.html - * - * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.dirigible.components.data.sources.manager; - -import java.util.Properties; -import org.eclipse.dirigible.components.data.sources.domain.DataSource; - -/** - * The Interface DataSourceInitializerContributor. - */ -public interface DataSourceInitializerContributor { - - /** - * Contribute. - * - * @param datasource the datasource - * @param properties the properties - */ - void contribute(DataSource datasource, Properties properties); - -} diff --git a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourcesManager.java b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourcesManager.java index eb4fa4f16c6..21dfc139e64 100644 --- a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourcesManager.java +++ b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DataSourcesManager.java @@ -14,6 +14,7 @@ import org.eclipse.dirigible.components.data.sources.domain.DataSource; import org.eclipse.dirigible.components.data.sources.service.CustomDataSourcesService; import org.eclipse.dirigible.components.data.sources.service.DataSourceService; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -76,7 +77,7 @@ public DataSourcesManager(DataSourceService datasourceService, CustomDataSources * * @return the default data source */ - public javax.sql.DataSource getDefaultDataSource() { + public DirigibleDataSource getDefaultDataSource() { return getDataSource(defaultDataSourceName); } @@ -86,7 +87,7 @@ public javax.sql.DataSource getDefaultDataSource() { * @param name the name * @return the data source */ - public javax.sql.DataSource getDataSource(String name) { + public DirigibleDataSource getDataSource(String name) { return dataSourceInitializer.isInitialized(name) ? dataSourceInitializer.getInitializedDataSource(name) : dataSourceInitializer.initialize(getDataSourceDefinition(name)); } @@ -116,7 +117,7 @@ public DataSource getDataSourceDefinition(String name) { * * @return the system data source */ - public javax.sql.DataSource getSystemDataSource() { + public DirigibleDataSource getSystemDataSource() { return getDataSource(systemDataSourceName); } diff --git a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleConnectionImpl.java b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleConnectionImpl.java new file mode 100644 index 00000000000..d39359eeb51 --- /dev/null +++ b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleConnectionImpl.java @@ -0,0 +1,307 @@ +package org.eclipse.dirigible.components.data.sources.manager; + +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.eclipse.dirigible.components.database.DirigibleConnection; + +import java.sql.*; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +class DirigibleConnectionImpl implements DirigibleConnection { + + private final Connection connection; + private final DatabaseSystem databaseSystem; + + DirigibleConnectionImpl(Connection connection, DatabaseSystem databaseSystem) { + this.connection = connection; + this.databaseSystem = databaseSystem; + } + + @Override + public Statement createStatement() throws SQLException { + return connection.createStatement(); + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + return connection.prepareStatement(sql); + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + return connection.prepareCall(sql); + } + + @Override + public String nativeSQL(String sql) throws SQLException { + return connection.nativeSQL(sql); + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + connection.setAutoCommit(autoCommit); + } + + @Override + public boolean getAutoCommit() throws SQLException { + return connection.getAutoCommit(); + } + + @Override + public void commit() throws SQLException { + connection.commit(); + } + + @Override + public void rollback() throws SQLException { + connection.rollback(); + } + + @Override + public void close() throws SQLException { + connection.close(); + } + + @Override + public boolean isClosed() throws SQLException { + return connection.isClosed(); + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return connection.getMetaData(); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + connection.setReadOnly(readOnly); + } + + @Override + public boolean isReadOnly() throws SQLException { + return connection.isReadOnly(); + } + + @Override + public void setCatalog(String catalog) throws SQLException { + connection.setCatalog(catalog); + } + + @Override + public String getCatalog() throws SQLException { + return connection.getCatalog(); + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + connection.setTransactionIsolation(level); + } + + @Override + public int getTransactionIsolation() throws SQLException { + return connection.getTransactionIsolation(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return connection.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + connection.clearWarnings(); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return connection.createStatement(resultSetType, resultSetConcurrency); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return connection.prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return connection.prepareCall(sql, resultSetType, resultSetConcurrency); + } + + @Override + public Map> getTypeMap() throws SQLException { + return connection.getTypeMap(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + connection.setTypeMap(map); + } + + @Override + public void setHoldability(int holdability) throws SQLException { + connection.setHoldability(holdability); + } + + @Override + public int getHoldability() throws SQLException { + return connection.getHoldability(); + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return connection.setSavepoint(); + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return connection.setSavepoint(name); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + connection.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + connection.releaseSavepoint(savepoint); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return connection.prepareStatement(sql, autoGeneratedKeys); + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return connection.prepareStatement(sql, columnIndexes); + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return connection.prepareStatement(sql, columnNames); + } + + @Override + public Clob createClob() throws SQLException { + return connection.createClob(); + } + + @Override + public Blob createBlob() throws SQLException { + return connection.createBlob(); + } + + @Override + public NClob createNClob() throws SQLException { + return connection.createNClob(); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return connection.createSQLXML(); + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return connection.isValid(timeout); + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + connection.setClientInfo(name, value); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + connection.setClientInfo(properties); + } + + @Override + public String getClientInfo(String name) throws SQLException { + return connection.getClientInfo(name); + } + + @Override + public Properties getClientInfo() throws SQLException { + return connection.getClientInfo(); + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return connection.createArrayOf(typeName, elements); + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return connection.createStruct(typeName, attributes); + } + + @Override + public void setSchema(String schema) throws SQLException { + connection.setSchema(schema); + } + + @Override + public String getSchema() throws SQLException { + return connection.getSchema(); + } + + @Override + public void abort(Executor executor) throws SQLException { + connection.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + connection.setNetworkTimeout(executor, milliseconds); + } + + @Override + public int getNetworkTimeout() throws SQLException { + return connection.getNetworkTimeout(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return connection.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return connection.isWrapperFor(iface); + } + + @Override + public DatabaseSystem getDatabaseSystem() { + return databaseSystem; + } + + @Override + public boolean isOfType(DatabaseSystem databaseSystem) { + return this.databaseSystem.isOfType(databaseSystem); + } + + @Override + public String toString() { + return "DirigibleConnectionImpl{" + "connection=" + connection + ", databaseSystem=" + databaseSystem + '}'; + } +} diff --git a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleDataSourceFactory.java b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleDataSourceFactory.java new file mode 100644 index 00000000000..7933f81a5c0 --- /dev/null +++ b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleDataSourceFactory.java @@ -0,0 +1,33 @@ +package org.eclipse.dirigible.components.data.sources.manager; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.eclipse.dirigible.components.database.ConnectionEnhancer; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.eclipse.dirigible.components.database.DirigibleDataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +class DirigibleDataSourceFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(DirigibleDataSourceFactory.class); + private final List connectionEnhancers; + + DirigibleDataSourceFactory(List connectionEnhancers) { + LOGGER.info("Loaded [{}] connection enhancers: {}", connectionEnhancers.size(), connectionEnhancers); + this.connectionEnhancers = connectionEnhancers; + } + + public DirigibleDataSource create(HikariConfig config, DatabaseSystem databaseSystem) { + HikariDataSource hikariDataSource = new HikariDataSource(config); + + DirigibleDataSource dataSource = new DirigibleDataSourceImpl(connectionEnhancers, hikariDataSource, databaseSystem); + Runtime.getRuntime() + .addShutdownHook(new Thread(dataSource::close)); + + return dataSource; + } +} diff --git a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/ManagedDataSource.java b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleDataSourceImpl.java similarity index 62% rename from components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/ManagedDataSource.java rename to components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleDataSourceImpl.java index d0bfc0de60e..fb5df97ac94 100644 --- a/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/ManagedDataSource.java +++ b/components/data/data-sources/src/main/java/org/eclipse/dirigible/components/data/sources/manager/DirigibleDataSourceImpl.java @@ -9,46 +9,52 @@ */ package org.eclipse.dirigible.components.data.sources.manager; +import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.LeakedConnectionsDoctor; -import org.eclipse.dirigible.components.api.security.UserFacade; +import org.eclipse.dirigible.components.database.ConnectionEnhancer; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.eclipse.dirigible.components.database.DirigibleConnection; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.util.List; +import java.util.stream.Collectors; /** * The WrappedDataSource of the standard JDBC {@link DataSource} object with added some additional * capabilities.. */ -public class ManagedDataSource implements DataSource { +class DirigibleDataSourceImpl implements DirigibleDataSource { /** The Constant LOGGER. */ - private static final Logger logger = LoggerFactory.getLogger(ManagedDataSource.class); + private static final Logger logger = LoggerFactory.getLogger(DirigibleDataSourceImpl.class); - /** The Constant DATABASE_NAME_HDB. */ - private static final String DATABASE_NAME_HDB = "HDB"; - /** The Constant DATABASE_NAME_SNOWFLAKE. */ - private static final String DATABASE_NAME_SNOWFLAKE = "Snowflake"; - - /** The original data source. */ - private final DataSource originalDataSource; - /** The database name. */ - private String databaseName; + private final List connectionEnhancers; + private final HikariDataSource originalDataSource; + private final DatabaseSystem databaseSystem; /** * Wrapper of the default datasource provided by the underlying platform It has some fault tolerance * features, which are not available by default in the popular JDBC drivers. * * @param originalDataSource the original data source + * @param databaseSystem database type */ - public ManagedDataSource(DataSource originalDataSource) { + DirigibleDataSourceImpl(List allConnectionEnhancers, HikariDataSource originalDataSource, + DatabaseSystem databaseSystem) { + this.connectionEnhancers = allConnectionEnhancers.stream() + .filter(e -> e.isApplicable(databaseSystem)) + .collect(Collectors.toList()); + logger.info("Filtered [{}] connection enhancers out of [{}] for system [{}]: {}", connectionEnhancers.size(), + allConnectionEnhancers.size(), databaseSystem, connectionEnhancers); this.originalDataSource = originalDataSource; + this.databaseSystem = databaseSystem; } /** @@ -58,54 +64,13 @@ public ManagedDataSource(DataSource originalDataSource) { * @throws SQLException the SQL exception */ @Override - public Connection getConnection() throws SQLException { + public DirigibleConnection getConnection() throws SQLException { Connection connection = originalDataSource.getConnection(); enhanceConnection(connection); LeakedConnectionsDoctor.registerConnection(connection); - return connection; - } - - /** - * Enhance connection. - * - * @param connection the connection - * @throws SQLException the SQL exception - */ - private void enhanceConnection(Connection connection) throws SQLException { - - if (this.databaseName == null) { - this.databaseName = connection.getMetaData() - .getDatabaseProductName(); - } - - // HANA - if (databaseName.equals(DATABASE_NAME_HDB)) { - Authentication authentication = SecurityContextHolder.getContext() - .getAuthentication(); - String userName; - if (authentication != null) { - userName = authentication.getName(); - } else { - userName = UserFacade.getName(); - } - if (logger.isDebugEnabled()) { - logger.debug("Setting APPLICATIONUSER:{} for connection: {}", userName, connection); - } - connection.setClientInfo("APPLICATIONUSER", userName); - - if (logger.isDebugEnabled()) { - logger.debug("Setting XS_APPLICATIONUSER:{} for connection: {}", userName, connection); - } - connection.setClientInfo("XS_APPLICATIONUSER", userName); - } - - // Snowflake - if (databaseName.equals(DATABASE_NAME_SNOWFLAKE)) { - connection.createStatement() - .executeQuery("ALTER SESSION SET JDBC_QUERY_RESULT_FORMAT='JSON'"); - } + return new DirigibleConnectionImpl(connection, databaseSystem); } /** @@ -117,13 +82,19 @@ private void enhanceConnection(Connection connection) throws SQLException { * @throws SQLException the SQL exception */ @Override - public Connection getConnection(String username, String password) throws SQLException { + public DirigibleConnection getConnection(String username, String password) throws SQLException { Connection connection = originalDataSource.getConnection(username, password); enhanceConnection(connection); LeakedConnectionsDoctor.registerConnection(connection); - return connection; + return new DirigibleConnectionImpl(connection, databaseSystem); + } + + private void enhanceConnection(Connection connection) throws SQLException { + for (ConnectionEnhancer enhancer : connectionEnhancers) { + enhancer.apply(connection); + } } /** @@ -207,7 +178,17 @@ public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedE } @Override - public String toString() { - return "ManagedDataSource{" + "databaseName='" + databaseName + '\'' + '}'; + public DatabaseSystem getDatabaseSystem() { + return databaseSystem; + } + + @Override + public boolean isOfType(DatabaseSystem databaseSystem) { + return this.databaseSystem.isOfType(databaseSystem); + } + + @Override + public void close() { + originalDataSource.close(); } } diff --git a/components/engine/engine-odata/src/test/java/org/eclipse/dirigible/components/odata/transformers/ODataDatabaseMetadataUtilTest.java b/components/engine/engine-odata/src/test/java/org/eclipse/dirigible/components/odata/transformers/ODataDatabaseMetadataUtilTest.java index a74feb5c55d..bcd9fe76134 100644 --- a/components/engine/engine-odata/src/test/java/org/eclipse/dirigible/components/odata/transformers/ODataDatabaseMetadataUtilTest.java +++ b/components/engine/engine-odata/src/test/java/org/eclipse/dirigible/components/odata/transformers/ODataDatabaseMetadataUtilTest.java @@ -9,20 +9,10 @@ */ package org.eclipse.dirigible.components.odata.transformers; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.Map; - -import javax.sql.DataSource; - import org.eclipse.dirigible.components.data.sources.manager.DataSourcesManager; -import org.eclipse.dirigible.components.data.structures.domain.Table; -import org.junit.jupiter.api.Assertions; +import org.eclipse.dirigible.components.database.DirigibleConnection; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; @@ -34,6 +24,11 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + /** * The Class DBMetadataUtilTest. */ @@ -96,11 +91,11 @@ public class ODataDatabaseMetadataUtilTest { /** The connection. */ @Mock - private Connection connection; + private DirigibleConnection connection; /** The data source. */ @Mock - private DataSource dataSource; + private DirigibleDataSource dataSource; /** The database meta data. */ @Mock @@ -142,6 +137,7 @@ public void setUp() throws SQLException { // testGetTableMetadata_columnTypeConversion("NotSupportedSQLType", null); // } + /** * Test artifact not found. * @@ -161,12 +157,6 @@ public void setUp() throws SQLException { // Mockito.verifyNoMoreInteractions(databaseMetaData); // } - /** - * Test get table metadata column type conversion. - * - * @param sqlType the sql type - * @param expectedEdmType the expected edm type - */ // private void testGetTableMetadata_columnTypeConversion(String sqlType, String expectedEdmType) { // try { // Mockito.when(connection.getMetaData()).thenReturn(databaseMetaData); @@ -217,12 +207,6 @@ public void setUp() throws SQLException { // // } - /** - * Mock DB meta data query no result. - * - * @param executedResultSet the executed result set - * @throws SQLException the SQL exception - */ // private void mockDBMetaDataQuery_noResult(ResultSet executedResultSet) throws SQLException { // ResultSet resultSet = Mockito.mock(ResultSet.class); // Mockito.when(executedResultSet).thenReturn(resultSet); diff --git a/modules/commons/commons-config/src/main/java/org/eclipse/dirigible/commons/config/DirigibleConfig.java b/modules/commons/commons-config/src/main/java/org/eclipse/dirigible/commons/config/DirigibleConfig.java index 174c94aa1bf..6f75942d631 100644 --- a/modules/commons/commons-config/src/main/java/org/eclipse/dirigible/commons/config/DirigibleConfig.java +++ b/modules/commons/commons-config/src/main/java/org/eclipse/dirigible/commons/config/DirigibleConfig.java @@ -20,6 +20,8 @@ */ public enum DirigibleConfig { + SNOWFLAKE_DATA_SOURCE_LIFESPAN_SECONDS("DIRIGIBLE_SNOWFLAKE_DATA_SOURCE_LIFESPAN_SECONDS", "540"), // 9 minutes + LEAKED_CONNECTIONS_MAX_IN_USE_SECONDS("DIRIGIBLE_LEAKED_CONNECTIONS_MAX_IN_USE_SECONDS", "180"), // 3 min by default LEAKED_CONNECTIONS_CHECK_INTERVAL_SECONDS("DIRIGIBLE_LEAKED_CONNECTIONS_CHECK_INTERVAL_SECONDS", "30"), diff --git a/modules/database/database-sql-h2/src/main/java/org/eclipse/dirigible/database/sql/dialects/h2/H2SqlDialectProvider.java b/modules/database/database-sql-h2/src/main/java/org/eclipse/dirigible/database/sql/dialects/h2/H2SqlDialectProvider.java index 07f79954d39..c56bd9dd7d2 100644 --- a/modules/database/database-sql-h2/src/main/java/org/eclipse/dirigible/database/sql/dialects/h2/H2SqlDialectProvider.java +++ b/modules/database/database-sql-h2/src/main/java/org/eclipse/dirigible/database/sql/dialects/h2/H2SqlDialectProvider.java @@ -9,6 +9,7 @@ */ package org.eclipse.dirigible.database.sql.dialects.h2; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlDialectProvider; @@ -17,14 +18,9 @@ */ public class H2SqlDialectProvider implements ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ @Override - public String getName() { - return "H2"; + public DatabaseSystem getDatabaseSystem() { + return DatabaseSystem.H2; } /** diff --git a/modules/database/database-sql-hana/pom.xml b/modules/database/database-sql-hana/pom.xml index 44d8aaf8560..a8784f21c9b 100644 --- a/modules/database/database-sql-hana/pom.xml +++ b/modules/database/database-sql-hana/pom.xml @@ -18,6 +18,10 @@ + + org.eclipse.dirigible + dirigible-components-data-sources + org.eclipse.dirigible dirigible-commons-config diff --git a/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaConnectionEnhancer.java b/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaConnectionEnhancer.java new file mode 100644 index 00000000000..6fbee6436d7 --- /dev/null +++ b/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaConnectionEnhancer.java @@ -0,0 +1,40 @@ +package org.eclipse.dirigible.database.sql.dialects.hana; + +import org.eclipse.dirigible.components.api.security.UserFacade; +import org.eclipse.dirigible.components.database.ConnectionEnhancer; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +import java.sql.Connection; +import java.sql.SQLException; + +@Component +class HanaConnectionEnhancer implements ConnectionEnhancer { + private static final Logger LOGGER = LoggerFactory.getLogger(HanaConnectionEnhancer.class); + + @Override + public boolean isApplicable(DatabaseSystem databaseSystem) { + return databaseSystem.isHANA(); + } + + @Override + public void apply(Connection connection) throws SQLException { + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); + String userName; + if (authentication != null) { + userName = authentication.getName(); + } else { + userName = UserFacade.getName(); + } + LOGGER.debug("Setting APPLICATIONUSER:{} for connection: {}", userName, connection); + connection.setClientInfo("APPLICATIONUSER", userName); + + LOGGER.debug("Setting XS_APPLICATIONUSER:{} for connection: {}", userName, connection); + connection.setClientInfo("XS_APPLICATIONUSER", userName); + } +} diff --git a/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaDatabaseConfigurator.java b/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaDatabaseConfigurator.java new file mode 100644 index 00000000000..5d5bfd6f678 --- /dev/null +++ b/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaDatabaseConfigurator.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Eclipse Dirigible contributors + * + * All rights reserved. This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.dirigible.database.sql.dialects.hana; + +import com.zaxxer.hikari.HikariConfig; +import org.eclipse.dirigible.components.database.DatabaseConfigurator; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +@Component +class HanaDatabaseConfigurator implements DatabaseConfigurator { + + @Override + public boolean isApplicable(DatabaseSystem databaseSystem) { + return databaseSystem.isHANA(); + } + + @Override + public void apply(HikariConfig config) { + config.setConnectionTestQuery("SELECT 1 FROM DUMMY"); // connection validation query + config.setKeepaliveTime(TimeUnit.MINUTES.toMillis(5)); // validation execution interval, must be bigger than idle timeout + } + +} diff --git a/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaSqlDialectProvider.java b/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaSqlDialectProvider.java index 45b9b6cc774..7f26e9fb460 100644 --- a/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaSqlDialectProvider.java +++ b/modules/database/database-sql-hana/src/main/java/org/eclipse/dirigible/database/sql/dialects/hana/HanaSqlDialectProvider.java @@ -9,6 +9,7 @@ */ package org.eclipse.dirigible.database.sql.dialects.hana; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlDialectProvider; @@ -17,14 +18,9 @@ */ public class HanaSqlDialectProvider implements ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ @Override - public String getName() { - return "HDB"; + public DatabaseSystem getDatabaseSystem() { + return DatabaseSystem.HANA; } /** diff --git a/modules/database/database-sql-mariadb/src/main/java/org/eclipse/dirigible/database/sql/dialects/mariadb/MariaDBSqlDialectProvider.java b/modules/database/database-sql-mariadb/src/main/java/org/eclipse/dirigible/database/sql/dialects/mariadb/MariaDBSqlDialectProvider.java index 089cff31a83..e96aaf2ac94 100644 --- a/modules/database/database-sql-mariadb/src/main/java/org/eclipse/dirigible/database/sql/dialects/mariadb/MariaDBSqlDialectProvider.java +++ b/modules/database/database-sql-mariadb/src/main/java/org/eclipse/dirigible/database/sql/dialects/mariadb/MariaDBSqlDialectProvider.java @@ -9,6 +9,7 @@ */ package org.eclipse.dirigible.database.sql.dialects.mariadb; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlDialectProvider; @@ -17,14 +18,9 @@ */ public class MariaDBSqlDialectProvider implements ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ @Override - public String getName() { - return "MariaDB"; + public DatabaseSystem getDatabaseSystem() { + return DatabaseSystem.MARIADB; } /** diff --git a/modules/database/database-sql-mongodb/src/main/java/org/eclipse/dirigible/database/sql/dialects/mongodb/MongoDBSqlDialectProvider.java b/modules/database/database-sql-mongodb/src/main/java/org/eclipse/dirigible/database/sql/dialects/mongodb/MongoDBSqlDialectProvider.java index 66d1678011e..7a6788ee630 100644 --- a/modules/database/database-sql-mongodb/src/main/java/org/eclipse/dirigible/database/sql/dialects/mongodb/MongoDBSqlDialectProvider.java +++ b/modules/database/database-sql-mongodb/src/main/java/org/eclipse/dirigible/database/sql/dialects/mongodb/MongoDBSqlDialectProvider.java @@ -9,6 +9,7 @@ */ package org.eclipse.dirigible.database.sql.dialects.mongodb; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlDialectProvider; @@ -17,14 +18,9 @@ */ public class MongoDBSqlDialectProvider implements ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ @Override - public String getName() { - return "MongoDB"; + public DatabaseSystem getDatabaseSystem() { + return DatabaseSystem.MONGODB; } /** diff --git a/modules/database/database-sql-mysql/src/main/java/org/eclipse/dirigible/database/sql/dialects/mysql/MySQLSqlDialectProvider.java b/modules/database/database-sql-mysql/src/main/java/org/eclipse/dirigible/database/sql/dialects/mysql/MySQLSqlDialectProvider.java index 33216c88d20..c93d9ff69ae 100644 --- a/modules/database/database-sql-mysql/src/main/java/org/eclipse/dirigible/database/sql/dialects/mysql/MySQLSqlDialectProvider.java +++ b/modules/database/database-sql-mysql/src/main/java/org/eclipse/dirigible/database/sql/dialects/mysql/MySQLSqlDialectProvider.java @@ -9,6 +9,7 @@ */ package org.eclipse.dirigible.database.sql.dialects.mysql; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlDialectProvider; @@ -17,14 +18,9 @@ */ public class MySQLSqlDialectProvider implements ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ @Override - public String getName() { - return "MySQL"; + public DatabaseSystem getDatabaseSystem() { + return DatabaseSystem.MYSQL; } /** diff --git a/modules/database/database-sql-postgres/src/main/java/org/eclipse/dirigible/database/sql/dialects/postgres/PostgresSqlDialectProvider.java b/modules/database/database-sql-postgres/src/main/java/org/eclipse/dirigible/database/sql/dialects/postgres/PostgresSqlDialectProvider.java index 5e49c3bd88a..62cec141e34 100644 --- a/modules/database/database-sql-postgres/src/main/java/org/eclipse/dirigible/database/sql/dialects/postgres/PostgresSqlDialectProvider.java +++ b/modules/database/database-sql-postgres/src/main/java/org/eclipse/dirigible/database/sql/dialects/postgres/PostgresSqlDialectProvider.java @@ -9,6 +9,7 @@ */ package org.eclipse.dirigible.database.sql.dialects.postgres; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlDialectProvider; @@ -17,14 +18,9 @@ */ public class PostgresSqlDialectProvider implements ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ @Override - public String getName() { - return "PostgreSQL"; + public DatabaseSystem getDatabaseSystem() { + return DatabaseSystem.POSTGRESQL; } /** diff --git a/modules/database/database-sql-snowflake/src/main/java/org/eclipse/dirigible/database/sql/dialects/snowflake/SnowflakeSqlDialectProvider.java b/modules/database/database-sql-snowflake/src/main/java/org/eclipse/dirigible/database/sql/dialects/snowflake/SnowflakeSqlDialectProvider.java index d5248094923..0a5854f8dc0 100644 --- a/modules/database/database-sql-snowflake/src/main/java/org/eclipse/dirigible/database/sql/dialects/snowflake/SnowflakeSqlDialectProvider.java +++ b/modules/database/database-sql-snowflake/src/main/java/org/eclipse/dirigible/database/sql/dialects/snowflake/SnowflakeSqlDialectProvider.java @@ -9,6 +9,7 @@ */ package org.eclipse.dirigible.database.sql.dialects.snowflake; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlDialectProvider; @@ -17,14 +18,9 @@ */ public class SnowflakeSqlDialectProvider implements ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ @Override - public String getName() { - return "Snowflake"; + public DatabaseSystem getDatabaseSystem() { + return DatabaseSystem.SNOWFLAKE; } /** diff --git a/modules/database/database-sql-sybase/src/main/java/org/eclipse/dirigible/database/sql/dialects/sybase/SybaseSqlDialectProvider.java b/modules/database/database-sql-sybase/src/main/java/org/eclipse/dirigible/database/sql/dialects/sybase/SybaseSqlDialectProvider.java index 6d0369c6b89..a98cf641233 100644 --- a/modules/database/database-sql-sybase/src/main/java/org/eclipse/dirigible/database/sql/dialects/sybase/SybaseSqlDialectProvider.java +++ b/modules/database/database-sql-sybase/src/main/java/org/eclipse/dirigible/database/sql/dialects/sybase/SybaseSqlDialectProvider.java @@ -9,6 +9,7 @@ */ package org.eclipse.dirigible.database.sql.dialects.sybase; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.ISqlDialectProvider; @@ -17,14 +18,9 @@ */ public class SybaseSqlDialectProvider implements ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ @Override - public String getName() { - return "Adaptive Server Enterprise"; + public DatabaseSystem getDatabaseSystem() { + return DatabaseSystem.SYBASE; } /** diff --git a/modules/database/database-sql/pom.xml b/modules/database/database-sql/pom.xml index ac6485aa3a8..083fd062994 100644 --- a/modules/database/database-sql/pom.xml +++ b/modules/database/database-sql/pom.xml @@ -23,6 +23,11 @@ org.eclipse.dirigible dirigible-commons-config + + org.eclipse.dirigible + dirigible-components-core-database + ${project.version} + diff --git a/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/ISqlDialectProvider.java b/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/ISqlDialectProvider.java index b3dabe084b1..895b3245ceb 100644 --- a/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/ISqlDialectProvider.java +++ b/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/ISqlDialectProvider.java @@ -9,17 +9,14 @@ */ package org.eclipse.dirigible.database.sql; +import org.eclipse.dirigible.components.database.DatabaseSystem; + /** * The Interface ISqlDialectProvider. */ public interface ISqlDialectProvider { - /** - * Gets the name. - * - * @return the name - */ - String getName(); + DatabaseSystem getDatabaseSystem(); /** * Gets the dialect. diff --git a/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/dialects/SqlDialectFactory.java b/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/dialects/SqlDialectFactory.java index 02b779429ea..6592701d15a 100644 --- a/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/dialects/SqlDialectFactory.java +++ b/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/dialects/SqlDialectFactory.java @@ -9,24 +9,50 @@ */ package org.eclipse.dirigible.database.sql.dialects; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.eclipse.dirigible.components.database.DatabaseSystemDeterminer; +import org.eclipse.dirigible.components.database.DirigibleConnection; +import org.eclipse.dirigible.components.database.DirigibleDataSource; +import org.eclipse.dirigible.database.sql.ISqlDialect; +import org.eclipse.dirigible.database.sql.ISqlDialectProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; -import org.eclipse.dirigible.database.sql.ISqlDialect; -import org.eclipse.dirigible.database.sql.ISqlDialectProvider; - /** * A factory for creating SqlDialect objects. */ public class SqlDialectFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(SqlDialectFactory.class); + /** The Constant ACCESS_MANAGERS. */ private static final ServiceLoader SQL_PROVIDERS = ServiceLoader.load(ISqlDialectProvider.class); + private static final Map dialectsBySystem = Collections.synchronizedMap(new HashMap<>()); + + static { + loadDefaultDialectsBySystem(); + } + + public static ISqlDialect getDialect(DataSource dataSource) throws SQLException { + if (dataSource instanceof DirigibleDataSource dds) { + return getDialect(dds); + } + + try (Connection connection = dataSource.getConnection()) { + return getDialect(connection); + } + } + /** * Gets the dialect. * @@ -34,39 +60,44 @@ public class SqlDialectFactory { * @return the dialect * @throws SQLException the SQL exception */ - public static final ISqlDialect getDialect(Connection connection) throws SQLException { - String productName = connection.getMetaData() - .getDatabaseProductName(); - ISqlDialect dialect = databaseTypeMappings.get(productName); - if (dialect == null) { - getDefaultDatabaseTypeMappings(); - dialect = databaseTypeMappings.get(productName); - if (dialect == null) { - throw new RuntimeException("Database dialect for " + productName + " is not avalable."); - } + public static ISqlDialect getDialect(Connection connection) throws SQLException { + if (connection instanceof DirigibleConnection dc) { + return getDialect(dc); } - return dialect; + DatabaseMetaData metaData = connection.getMetaData(); + String jdbcUrl = metaData.getURL(); + String driver = metaData.getDriverName(); + DatabaseSystem databaseSystem = DatabaseSystemDeterminer.determine(jdbcUrl, driver); + + return getDialect(databaseSystem); } + public static ISqlDialect getDialect(DirigibleConnection connection) throws SQLException { + return getDialect(connection.getDatabaseSystem()); + } + public static ISqlDialect getDialect(DirigibleDataSource dataSource) throws SQLException { + DatabaseSystem databaseSystem = dataSource.getDatabaseSystem(); + return getDialect(databaseSystem); + } - /** The Constant databaseTypeMappings. */ - // Lifted from Activiti - static Map databaseTypeMappings = Collections.synchronizedMap(new HashMap()); - static { - getDefaultDatabaseTypeMappings(); + public static ISqlDialect getDialect(DatabaseSystem databaseSystem) throws SQLException { + ISqlDialect dialect = dialectsBySystem.get(databaseSystem); + if (dialect == null) { + loadDefaultDialectsBySystem(); + dialect = dialectsBySystem.get(databaseSystem); + if (dialect == null) { + throw new IllegalStateException("Database dialect for [" + databaseSystem + "] is not available."); + } + } + LOGGER.debug("Loaded dialect [{}] for [{}]", dialect, databaseSystem); + return dialect; } - /** - * Gets the default database type mappings. - * - * @return the default database type mappings - */ - protected static Map getDefaultDatabaseTypeMappings() { - for (ISqlDialectProvider next : SQL_PROVIDERS) { - databaseTypeMappings.put(next.getName(), next.getDialect()); + private static void loadDefaultDialectsBySystem() { + for (ISqlDialectProvider provider : SQL_PROVIDERS) { + dialectsBySystem.put(provider.getDatabaseSystem(), provider.getDialect()); } - return databaseTypeMappings; } } diff --git a/modules/database/pom.xml b/modules/database/pom.xml index 084111b3aae..8f9826b5d26 100644 --- a/modules/database/pom.xml +++ b/modules/database/pom.xml @@ -40,4 +40,14 @@ - \ No newline at end of file + + + + org.eclipse.dirigible + dirigible-components-data-sources + ${project.version} + + + + + diff --git a/modules/odata/odata-core-test/src/test/java/org/eclipse/dirigible/engine/odata2/sql/builder/SQLSelectBuilderTest.java b/modules/odata/odata-core-test/src/test/java/org/eclipse/dirigible/engine/odata2/sql/builder/SQLSelectBuilderTest.java index 52d1992ba24..6e2ca54a0e2 100644 --- a/modules/odata/odata-core-test/src/test/java/org/eclipse/dirigible/engine/odata2/sql/builder/SQLSelectBuilderTest.java +++ b/modules/odata/odata-core-test/src/test/java/org/eclipse/dirigible/engine/odata2/sql/builder/SQLSelectBuilderTest.java @@ -25,8 +25,8 @@ import org.apache.olingo.odata2.core.ep.entry.ODataEntryImpl; import org.apache.olingo.odata2.core.uri.UriParserImpl; import org.eclipse.dirigible.commons.config.Configuration; +import org.eclipse.dirigible.components.database.DatabaseSystem; import org.eclipse.dirigible.engine.odata2.sql.binding.EdmTableBindingProvider; -import org.eclipse.dirigible.engine.odata2.sql.builder.SQLContext.DatabaseProduct; import org.eclipse.dirigible.engine.odata2.sql.clause.SQLSelectClause; import org.eclipse.dirigible.engine.odata2.sql.edm.*; import org.eclipse.dirigible.engine.odata2.sql.mapping.DefaultEdmTableMappingProvider; @@ -220,7 +220,7 @@ public void testBuildSelectWithOrderBy() throws Exception { "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\", T0.LOGSTART AS \"LOGSTART_T0\", T0.LOGEND AS \"LOGEND_T0\", " + "T0.SENDER AS \"SENDER_T0\", T0.RECEIVER AS \"RECEIVER_T0\", T0.STATUS AS \"STATUS_T0\", T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" " + "FROM MPLHEADER AS T0 " + "ORDER BY T0.STATUS ASC, T0.LOGSTART DESC" + SERVER_SIDE_PAGING_DEFAULT_SUFFIX; - testBuildSelectStatement(params, context.getDatabaseProduct(), expectedSelectStatment); + testBuildSelectStatement(params, context.getDatabaseSystem(), expectedSelectStatment); } /** @@ -236,7 +236,7 @@ public void testBuildSelectWithoutOrderBy() throws Exception { "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\", T0.LOGSTART AS \"LOGSTART_T0\", T0.LOGEND AS \"LOGEND_T0\", " + "T0.SENDER AS \"SENDER_T0\", T0.RECEIVER AS \"RECEIVER_T0\", T0.STATUS AS \"STATUS_T0\", T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" " + "FROM MPLHEADER AS T0 " + "ORDER BY T0.MESSAGEGUID ASC" + SERVER_SIDE_PAGING_DEFAULT_SUFFIX; - testBuildSelectStatement(params, context.getDatabaseProduct(), expectedSelectStatment); + testBuildSelectStatement(params, context.getDatabaseSystem(), expectedSelectStatment); } /** @@ -255,7 +255,7 @@ public void testBuildSelectWithSkip0AndTop() throws Exception { + "T0.SENDER AS \"SENDER_T0\", T0.RECEIVER AS \"RECEIVER_T0\", T0.STATUS AS \"STATUS_T0\", " + "T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" " + "FROM MPLHEADER AS T0 ORDER BY T0.MESSAGEGUID ASC " + "FETCH FIRST 10 ROWS ONLY"; - testBuildSelectStatement(params, context.getDatabaseProduct(), expectedSelectStatment); + testBuildSelectStatement(params, context.getDatabaseSystem(), expectedSelectStatment); } /** @@ -271,7 +271,7 @@ public void testBuildSelectWithSelect() throws Exception { String expectedSelectStatment = "SELECT T0.STATUS AS \"STATUS_T0\", T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 " + "ORDER BY T0.STATUS ASC, T0.LOGSTART DESC" + SERVER_SIDE_PAGING_DEFAULT_SUFFIX; - testBuildSelectStatement(params, context.getDatabaseProduct(), expectedSelectStatment); + testBuildSelectStatement(params, context.getDatabaseSystem(), expectedSelectStatment); } /** @@ -288,7 +288,7 @@ public void testBuildSelectWithSelectPrimaryKey() throws Exception { String expectedSelectStatment = "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.STATUS ASC, T0.LOGSTART DESC" + SERVER_SIDE_PAGING_DEFAULT_SUFFIX; - testBuildSelectStatement(params, context.getDatabaseProduct(), expectedSelectStatment); + testBuildSelectStatement(params, context.getDatabaseSystem(), expectedSelectStatment); } /** @@ -306,7 +306,7 @@ public void testBuildSelectWithSelectAttribute_PrimaryKeyIsAlsoSelected() throws String expectedSelectStatment = "SELECT T0.STATUS AS \"STATUS_T0\", T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.STATUS ASC, T0.LOGSTART DESC" + SERVER_SIDE_PAGING_DEFAULT_SUFFIX; - testBuildSelectStatement(params, context.getDatabaseProduct(), expectedSelectStatment); + testBuildSelectStatement(params, context.getDatabaseSystem(), expectedSelectStatment); } /** @@ -318,7 +318,7 @@ public void testBuildSelectWithSelectAttribute_PrimaryKeyIsAlsoSelected() throws public void testBuildSelectStatementWithSelectTop() throws ODataException { String expectedSelectStmnt = "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.LOGSTART " + "DESC FETCH FIRST 12 ROWS ONLY"; - testBuildSelectStatementWithSelectTop(context.getDatabaseProduct(), 12, expectedSelectStmnt); + testBuildSelectStatementWithSelectTop(context.getDatabaseSystem(), 12, expectedSelectStmnt); } /** @@ -330,7 +330,7 @@ public void testBuildSelectStatementWithSelectTop() throws ODataException { public void testBuildSelectStatementWithSelectTopPostgres() throws ODataException { String expectedSelectStatement = "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.LOGSTART DESC LIMIT 4"; - testBuildSelectStatementWithSelectTop(DatabaseProduct.POSTGRE_SQL, 4, expectedSelectStatement); + testBuildSelectStatementWithSelectTop(DatabaseSystem.POSTGRESQL, 4, expectedSelectStatement); } /** @@ -342,7 +342,7 @@ public void testBuildSelectStatementWithSelectTopPostgres() throws ODataExceptio public void testBuildSelectStatementWithSelectTopSybase() throws ODataException { String expectedSelectStatement = "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.LOGSTART DESC LIMIT 5"; - testBuildSelectStatementWithSelectTop(DatabaseProduct.SYBASE_ASE, 5, expectedSelectStatement); + testBuildSelectStatementWithSelectTop(DatabaseSystem.SYBASE, 5, expectedSelectStatement); } /** @@ -354,25 +354,17 @@ public void testBuildSelectStatementWithSelectTopSybase() throws ODataException public void testBuildSelectStatementWithSelectTopHANA() throws ODataException { String expectedSelectStatement = "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.LOGSTART DESC LIMIT 3"; - testBuildSelectStatementWithSelectTop(DatabaseProduct.HANA, 3, expectedSelectStatement); + testBuildSelectStatementWithSelectTop(DatabaseSystem.HANA, 3, expectedSelectStatement); } - /** - * Test build select statement with select top. - * - * @param dbType the db type - * @param top the top - * @param expectedSelectStatement the expected select statement - * @throws ODataException the o data exception - */ - private void testBuildSelectStatementWithSelectTop(DatabaseProduct dbType, Integer top, String expectedSelectStatement) + private void testBuildSelectStatementWithSelectTop(DatabaseSystem databaseSystem, Integer top, String expectedSelectStatement) throws ODataException { Map params = new HashMap<>(); params.put("$select", "MessageGuid"); params.put("$orderby", "LogStart desc"); params.put("$top", top.toString()); - testBuildSelectStatement(params, dbType, expectedSelectStatement); + testBuildSelectStatement(params, databaseSystem, expectedSelectStatement); } /** @@ -384,7 +376,7 @@ private void testBuildSelectStatementWithSelectTop(DatabaseProduct dbType, Integ public void testBuildSelectStatementWithSelectTopAndSkipHANA() throws ODataException { String expectedSelectStatement = "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.STATUS ASC, T0.LOGSTART DESC LIMIT 2 OFFSET 5"; - testBuildSelectStatementWithSelectTopAndSkip(DatabaseProduct.HANA, 2, 5, expectedSelectStatement); + testBuildSelectStatementWithSelectTopAndSkip(DatabaseSystem.HANA, 2, 5, expectedSelectStatement); } /** @@ -396,7 +388,7 @@ public void testBuildSelectStatementWithSelectTopAndSkipHANA() throws ODataExcep public void testBuildSelectStatementWithSelectTopAndSkipSybase() throws ODataException { String expectedSelectStatement = "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.STATUS ASC, T0.LOGSTART DESC LIMIT 10 OFFSET 20"; - testBuildSelectStatementWithSelectTopAndSkip(DatabaseProduct.SYBASE_ASE, 10, 20, expectedSelectStatement); + testBuildSelectStatementWithSelectTopAndSkip(DatabaseSystem.SYBASE, 10, 20, expectedSelectStatement); } /** @@ -408,19 +400,10 @@ public void testBuildSelectStatementWithSelectTopAndSkipSybase() throws ODataExc public void testBuildSelectStatementWithSelectTopAndSkipPostgre() throws ODataException { String expectedSelectStatement = "SELECT T0.MESSAGEGUID AS \"MESSAGEGUID_T0\" FROM MPLHEADER AS T0 ORDER BY T0.STATUS ASC, T0.LOGSTART DESC LIMIT 2 OFFSET 6"; - testBuildSelectStatementWithSelectTopAndSkip(DatabaseProduct.POSTGRE_SQL, 2, 6, expectedSelectStatement); + testBuildSelectStatementWithSelectTopAndSkip(DatabaseSystem.POSTGRESQL, 2, 6, expectedSelectStatement); } - /** - * Test build select statement with select top and skip. - * - * @param dbType the db type - * @param top the top - * @param skip the skip - * @param expectedSelectStatement the expected select statement - * @throws ODataException the o data exception - */ - private void testBuildSelectStatementWithSelectTopAndSkip(DatabaseProduct dbType, Integer top, Integer skip, + private void testBuildSelectStatementWithSelectTopAndSkip(DatabaseSystem databaseSystem, Integer top, Integer skip, String expectedSelectStatement) throws ODataException { Map params = new HashMap<>(); params.put("$select", "MessageGuid"); @@ -428,24 +411,16 @@ private void testBuildSelectStatementWithSelectTopAndSkip(DatabaseProduct dbType params.put("$skip", skip.toString()); params.put("$top", top.toString()); - testBuildSelectStatement(params, dbType, expectedSelectStatement); + testBuildSelectStatement(params, databaseSystem, expectedSelectStatement); } - /** - * Test build select statement. - * - * @param uriParams the uri params - * @param dbType the db type - * @param expectedSelectStatment the expected select statment - * @throws ODataException the o data exception - */ - private void testBuildSelectStatement(Map uriParams, DatabaseProduct dbType, String expectedSelectStatment) + private void testBuildSelectStatement(Map uriParams, DatabaseSystem databaseSystem, String expectedSelectStatment) throws ODataException { Configuration.set("DIRIGIBLE_DATABASE_NAMES_CASE_SENSITIVE", "false"); PathSegment ps1 = createPathSegment(); UriInfo uriInfo = uriParser.parse(Collections.singletonList(ps1), uriParams); SQLSelectBuilder selectBuilder = builder.buildSelectEntitySetQuery(uriInfo, null); - SQLContext context = new SQLContext(dbType); + SQLContext context = new SQLContext(databaseSystem); assertEquals(expectedSelectStatment, selectBuilder.buildSelect(context)); } @@ -618,7 +593,7 @@ public void testSelectWithParametersForEntityWhenCalcViewAndHanaDb() throws Exce UriInfo uriInfo = uriParser.parse(Collections.singletonList(ps), params); SQLSelectBuilder q = builder.buildSelectEntityQuery(uriInfo, null); - SQLContext context = new SQLContext(DatabaseProduct.HANA); + SQLContext context = new SQLContext(DatabaseSystem.HANA); List selectSql = Arrays.asList(q.buildSelect(context) .split("SELECT|FROM|WHERE")); @@ -661,7 +636,7 @@ public void testSelectWithParametersForEntitySetWhenCalcViewAndHanaDb() throws E UriInfo uriInfo = uriParser.parse(Collections.singletonList(ps), params); SQLSelectBuilder q = builder.buildSelectEntitySetQuery(uriInfo, null); - SQLContext context = new SQLContext(DatabaseProduct.HANA); + SQLContext context = new SQLContext(DatabaseSystem.HANA); List selectSql = Arrays.asList(q.buildSelect(context) .split("SELECT|FROM|WHERE|ORDER BY|LIMIT")); diff --git a/modules/odata/odata-core/pom.xml b/modules/odata/odata-core/pom.xml index 29b2c75281f..f27b7505d92 100644 --- a/modules/odata/odata-core/pom.xml +++ b/modules/odata/odata-core/pom.xml @@ -28,6 +28,11 @@ + + org.eclipse.dirigible + dirigible-components-core-database + ${project.version} + org.eclipse.dirigible dirigible-commons-config diff --git a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/builder/SQLContext.java b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/builder/SQLContext.java index 3f3d05e23aa..2c056f1d553 100644 --- a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/builder/SQLContext.java +++ b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/builder/SQLContext.java @@ -10,8 +10,11 @@ package org.eclipse.dirigible.engine.odata2.sql.builder; import org.apache.olingo.odata2.api.processor.ODataContext; +import org.eclipse.dirigible.components.database.DatabaseSystem; +import org.eclipse.dirigible.components.database.DatabaseSystemDeterminer; import org.eclipse.dirigible.engine.odata2.sql.api.OData2Exception; +import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; @@ -22,96 +25,37 @@ */ public class SQLContext { - /** - * The Enum DatabaseProduct. - */ - public enum DatabaseProduct { - - /** The derby. */ - DERBY(false), - /** The sybase ase. */ - SYBASE_ASE(false), - /** The postgre sql. */ - POSTGRE_SQL(true), - /** The h2. */ - H2(false), - /** The hana. */ - HANA(true); - - /** The case sensitive. */ - private boolean caseSensitive; - - /** - * Instantiates a new database product. - * - * @param caseSensitive the case sensitive - */ - DatabaseProduct(boolean caseSensitive) { - this.caseSensitive = caseSensitive; - } - - /** - * Checks if is case sensitive. - * - * @return true, if is case sensitive - */ - public boolean isCaseSensitive() { - return caseSensitive; - } - } - /** The database product. */ - private final DatabaseProduct databaseProduct; + private final DatabaseSystem databaseSystem; /** The odata context. */ private ODataContext odataContext; - /** The metadata. */ - private DatabaseMetaData metadata; - /** * Instantiates a new SQL context. */ public SQLContext() { - databaseProduct = DatabaseProduct.DERBY; + databaseSystem = DatabaseSystem.DERBY; } /** * Instantiates a new SQL context. * - * @param metadata the metadata + * @param connection the connection * @param odataContext the odata context */ - public SQLContext(final DatabaseMetaData metadata, final ODataContext odataContext) { - this.metadata = metadata; + public SQLContext(final Connection connection, final ODataContext odataContext) throws SQLException { this.odataContext = odataContext; - String dbProductName = getDatabaseName(metadata); - if (dbProductName.toLowerCase() - .contains("derby")) { - databaseProduct = DatabaseProduct.DERBY; - } else if (dbProductName.toLowerCase() - .contains("adaptive server enterprise")) { - databaseProduct = DatabaseProduct.SYBASE_ASE; - } else if (dbProductName.toLowerCase() - .contains("postgre")) { - databaseProduct = DatabaseProduct.POSTGRE_SQL; - } else if (dbProductName.toLowerCase() - .contains("h2")) { - databaseProduct = DatabaseProduct.H2; - } else if (dbProductName.toLowerCase() - .contains("hdb")) { - databaseProduct = DatabaseProduct.HANA; - } else - throw new OData2Exception("Unsupported database " + dbProductName, SERVICE_UNAVAILABLE); + this.databaseSystem = DatabaseSystemDeterminer.determine(connection); } /** * Instantiates a new SQL context. * - * @param databaseProduct the database product name + * @param databaseSystem the database system */ - public SQLContext(final DatabaseProduct databaseProduct) { - this.databaseProduct = databaseProduct; + public SQLContext(final DatabaseSystem databaseSystem) { + this.databaseSystem = databaseSystem; } /** @@ -119,8 +63,8 @@ public SQLContext(final DatabaseProduct databaseProduct) { * * @return the database product */ - public DatabaseProduct getDatabaseProduct() { - return databaseProduct; + public DatabaseSystem getDatabaseSystem() { + return databaseSystem; } /** @@ -132,15 +76,6 @@ public ODataContext getOdataContext() { return odataContext; } - /** - * Gets the database metadata. - * - * @return the database metadata - */ - public DatabaseMetaData getDatabaseMetadata() { - return metadata; - } - /** * Gets the database name. * diff --git a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLGroupByClause.java b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLGroupByClause.java index 832340af0fe..a4a47bb2296 100644 --- a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLGroupByClause.java +++ b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLGroupByClause.java @@ -72,11 +72,11 @@ public String groupByClauseBuilder(SQLContext context) throws EdmException { groupByClause.append(", "); } - if (context == null || context.getDatabaseProduct() != null) { + if (context == null || context.getDatabaseSystem() != null) { groupByClause.append(query.getSQLTableColumn(entityType, prop)); } else { groupByClause.append(query.getSQLTableColumnAlias(entityType, prop)); // this gives the correct "group by" column name for - // Open SQL + // Open SQL } } return groupByClause.toString(); diff --git a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLOrderByClause.java b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLOrderByClause.java index abff5103cb0..47e9cd369b1 100644 --- a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLOrderByClause.java +++ b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLOrderByClause.java @@ -64,7 +64,6 @@ public SQLOrderByClause(final SQLSelectBuilder query, final EdmEntityType orderB this.entityType = orderByEntityType; } - /** * Evaluate. * @@ -143,15 +142,13 @@ protected String orderByClause(SQLContext context, OrderExpression orderBy) thro EdmProperty prop; StringBuilder orderByClause = new StringBuilder(); - if (expression instanceof MemberExpression) { - MemberExpression memberExpression = (MemberExpression) expression; + if (expression instanceof MemberExpression memberExpression) { CommonExpression pathExpression = memberExpression.getPath(); entityType = (EdmStructuralType) pathExpression.getEdmType(); PropertyExpression propertyExpression = (PropertyExpression) memberExpression.getProperty(); prop = (EdmProperty) propertyExpression.getEdmProperty(); - } else if (expression instanceof PropertyExpression) { - PropertyExpression propertyExpression = (PropertyExpression) expression; + } else if (expression instanceof PropertyExpression propertyExpression) { prop = (EdmProperty) propertyExpression.getEdmProperty(); entityType = this.entityType; } else { @@ -165,7 +162,7 @@ protected String orderByClause(SQLContext context, OrderExpression orderBy) thro throw new OData2Exception(INTERNAL_SERVER_ERROR); } - if ((context == null || context.getDatabaseProduct() != null)) { + if ((context == null || context.getDatabaseSystem() != null)) { if (isPropertyParameter(prop, query, entityType)) { orderByClause.append(query.getSQLTableColumnAlias(entityType, prop)); } else { @@ -173,7 +170,7 @@ protected String orderByClause(SQLContext context, OrderExpression orderBy) thro } } else { orderByClause.append(query.getSQLTableColumnAlias(entityType, prop)); // This gives the correct "order by" column name for Open - // SQL + // SQL } orderByClause.append(" ") .append(orderBy.getSortOrder() == SortOrder.asc ? "ASC" : "DESC"); diff --git a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLSelectClause.java b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLSelectClause.java index 06e0a5a2679..6017e6117f6 100644 --- a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLSelectClause.java +++ b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/clause/SQLSelectClause.java @@ -20,7 +20,6 @@ import org.eclipse.dirigible.engine.odata2.sql.binding.EdmTableBinding; import org.eclipse.dirigible.engine.odata2.sql.builder.EdmUtils; import org.eclipse.dirigible.engine.odata2.sql.builder.SQLContext; -import org.eclipse.dirigible.engine.odata2.sql.builder.SQLContext.DatabaseProduct; import org.eclipse.dirigible.engine.odata2.sql.builder.SQLUtils; import java.util.*; @@ -525,7 +524,8 @@ private String buildLimit(final SQLContext context) { return EMPTY_STRING; String selectPredicate = EMPTY_STRING; if (top > 0) { - if (context.getDatabaseProduct() == DatabaseProduct.DERBY) { + if (context.getDatabaseSystem() + .isDerby()) { // Derby: FETCH { FIRST | NEXT } [integer-literal] {ROW | ROWS} ONLY return String.format("FETCH FIRST %d ROWS ONLY", top); } else { @@ -558,8 +558,8 @@ private String buildFrom(final SQLContext context) throws EdmException { if (isSelectTarget(type)) { boolean isView = EdmTableBinding.DataStructureType.VIEW == targetDataStructureType; boolean isCalculationView = EdmTableBinding.DataStructureType.CALC_VIEW == targetDataStructureType; - boolean isHanaDatabase = context.getDatabaseProduct() - .equals(DatabaseProduct.HANA); + boolean isHanaDatabase = context.getDatabaseSystem() + .isHANA(); if ((isView || (isCalculationView && isHanaDatabase)) && !this.parameters.isEmpty()) { addInputParamsAsStatementParams(parameters); tables.add(query.getSQLTableName(target) + buildTargetParameters() + " AS " + escapedTableAlias); diff --git a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/processor/AbstractSQLProcessor.java b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/processor/AbstractSQLProcessor.java index 66dd5c8ffee..2219561a93b 100644 --- a/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/processor/AbstractSQLProcessor.java +++ b/modules/odata/odata-core/src/main/java/org/eclipse/dirigible/engine/odata2/sql/processor/AbstractSQLProcessor.java @@ -184,7 +184,7 @@ protected int doCountEntitySet(SQLSelectBuilder sqlQuery, final Connection conne * @throws SQLException the SQL exception */ protected SQLContext createSQLContext(final Connection connection) throws SQLException { - return new SQLContext(connection.getMetaData(), this.getContext()); + return new SQLContext(connection, this.getContext()); } /** diff --git a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/DirigibleCleaner.java b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/DirigibleCleaner.java index 07204ce7091..189f7190e72 100644 --- a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/DirigibleCleaner.java +++ b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/DirigibleCleaner.java @@ -11,6 +11,7 @@ import org.eclipse.dirigible.commons.config.DirigibleConfig; import org.eclipse.dirigible.components.data.sources.manager.DataSourcesManager; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.dialects.SqlDialectFactory; import org.eclipse.dirigible.repository.api.IRepository; @@ -70,17 +71,17 @@ private void deleteDatabases() { * memory. */ private void deleteDirigibleDBData() { - DataSource defaultDataSource = dataSourcesManager.getDefaultDataSource(); + DirigibleDataSource defaultDataSource = dataSourcesManager.getDefaultDataSource(); dropAllTablesInSchema(defaultDataSource); dropAllSequencesInSchema(defaultDataSource); - DataSource systemDataSource = dataSourcesManager.getSystemDataSource(); + DirigibleDataSource systemDataSource = dataSourcesManager.getSystemDataSource(); deleteAllTablesDataInSchema(systemDataSource); deleteSchemas(defaultDataSource); } - private void deleteAllTablesDataInSchema(DataSource dataSource) { + private void deleteAllTablesDataInSchema(DirigibleDataSource dataSource) { Set tables = getAllTables(dataSource); for (int idx = 0; idx < 4; idx++) { // execute it a few times due to constraint violations @@ -88,7 +89,7 @@ private void deleteAllTablesDataInSchema(DataSource dataSource) { while (iterator.hasNext()) { String table = iterator.next(); try (Connection connection = dataSource.getConnection()) { - String sql = SqlDialectFactory.getDialect(connection) + String sql = SqlDialectFactory.getDialect(dataSource) .delete() .from(table) .build(); @@ -104,7 +105,7 @@ private void deleteAllTablesDataInSchema(DataSource dataSource) { } } - private void dropAllSequencesInSchema(DataSource dataSource) { + private void dropAllSequencesInSchema(DirigibleDataSource dataSource) { List sequences = getAllSequences(dataSource); LOGGER.info("Will drop [{}] sequences from data source [{}]. Sequences: {}", sequences.size(), dataSource, sequences); @@ -113,7 +114,7 @@ private void dropAllSequencesInSchema(DataSource dataSource) { while (iterator.hasNext()) { String sequence = iterator.next(); try (Connection connection = dataSource.getConnection()) { - String sql = SqlDialectFactory.getDialect(connection) + String sql = SqlDialectFactory.getDialect(dataSource) .drop() .sequence(sequence) .build(); @@ -144,7 +145,7 @@ private List getAllSequences(DataSource dataSource) { } } - private void dropAllTablesInSchema(DataSource dataSource) { + private void dropAllTablesInSchema(DirigibleDataSource dataSource) { Set tables = getAllTables(dataSource); LOGGER.info("Will drop [{}] tables from data source [{}]. Tables: {}", tables.size(), dataSource, tables); @@ -153,7 +154,7 @@ private void dropAllTablesInSchema(DataSource dataSource) { while (iterator.hasNext()) { String tableName = iterator.next(); try (Connection connection = dataSource.getConnection()) { - String sql = SqlDialectFactory.getDialect(connection) + String sql = SqlDialectFactory.getDialect(dataSource) .drop() .table(tableName) .cascade(true) @@ -185,7 +186,7 @@ private Set getAllTables(DataSource dataSource) { } } - private void deleteSchemas(DataSource dataSource) { + private void deleteSchemas(DirigibleDataSource dataSource) { Set schemas = getSchemas(dataSource); schemas.remove("PUBLIC"); schemas.remove("public"); @@ -223,10 +224,10 @@ private Set getSchemas(DataSource dataSource, String sql) throws SQLExce } } - private void deleteSchema(String schema, DataSource dataSource) { + private void deleteSchema(String schema, DirigibleDataSource dataSource) { LOGGER.info("Will drop schema [{}] from data source [{}]", schema, dataSource); try (Connection connection = dataSource.getConnection()) { - ISqlDialect dialect = SqlDialectFactory.getDialect(connection); + ISqlDialect dialect = SqlDialectFactory.getDialect(dataSource); String sql = dialect.drop() .schema(schema) .cascade(true) diff --git a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/TenantCreator.java b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/TenantCreator.java index d1914a037eb..32c6d4fbafe 100644 --- a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/TenantCreator.java +++ b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/TenantCreator.java @@ -58,6 +58,9 @@ private Tenant createTenantEntity(DirigibleTestTenant tenant) { public boolean isTenantProvisioned(DirigibleTestTenant tenant) { Tenant foundTenant = tenantService.findById(tenant.getId()) .orElseThrow(() -> new IllegalStateException("Tenant [" + tenant + "] doesn't exist")); - return foundTenant.getStatus() == TenantStatus.PROVISIONED; + + boolean tenantProvisioned = foundTenant.getStatus() == TenantStatus.PROVISIONED; + LOGGER.info("Tenant [{}] provisioned [{}]", foundTenant, tenantProvisioned); + return tenantProvisioned; } } diff --git a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/api/java/CsvimIT.java b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/api/java/CsvimIT.java index 3e0e9146e55..592235448e9 100644 --- a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/api/java/CsvimIT.java +++ b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/api/java/CsvimIT.java @@ -10,6 +10,7 @@ package org.eclipse.dirigible.integration.tests.api.java; import org.eclipse.dirigible.components.data.sources.manager.DataSourcesManager; +import org.eclipse.dirigible.components.database.DirigibleDataSource; import org.eclipse.dirigible.database.sql.DataType; import org.eclipse.dirigible.database.sql.ISqlDialect; import org.eclipse.dirigible.database.sql.SqlFactory; @@ -104,9 +105,9 @@ void testImportData() throws SQLException { } private void createUndefiniedTable() { - DataSource defaultDataSource = dataSourcesManager.getDefaultDataSource(); + DirigibleDataSource defaultDataSource = dataSourcesManager.getDefaultDataSource(); try (Connection connection = defaultDataSource.getConnection()) { - ISqlDialect dialect = SqlDialectFactory.getDialect(defaultDataSource.getConnection()); + ISqlDialect dialect = SqlDialectFactory.getDialect(defaultDataSource); String sql = dialect.create() .table(UNDEFINIED_TABLE_NAME) .column("READER_ID", DataType.INTEGER, true) @@ -150,9 +151,9 @@ private boolean isTableExists(String tableName) throws SQLException { } private List getAllData(String tableName) { - DataSource defaultDataSource = dataSourcesManager.getDefaultDataSource(); + DirigibleDataSource defaultDataSource = dataSourcesManager.getDefaultDataSource(); try (Connection connection = defaultDataSource.getConnection()) { - String sql = SqlDialectFactory.getDialect(connection) + String sql = SqlDialectFactory.getDialect(defaultDataSource) .select() .from(tableName) .build(); diff --git a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/BPMStarterTemplateIT.java b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/BPMStarterTemplateIT.java index fcb16193030..037931465f9 100644 --- a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/BPMStarterTemplateIT.java +++ b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/BPMStarterTemplateIT.java @@ -87,7 +87,7 @@ void testCreateProjectFromTemplate() { browser.enterTextInElementById(PARAM_2_ID, PARAM_2_VALUE); browser.clickOnElementContainingText(HtmlElementType.BUTTON, TRIGGER_BUTTON_TEXT); - await().atMost(20, TimeUnit.SECONDS) + await().atMost(30, TimeUnit.SECONDS) .until(() -> consoleLogAsserter.containsMessage(EXPECTED_TASK_LOGGED_MESSAGE, Level.INFO)); } diff --git a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/MultitenancyIT.java b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/MultitenancyIT.java index cd0a3177bfd..8d08a618ea2 100644 --- a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/MultitenancyIT.java +++ b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/MultitenancyIT.java @@ -51,9 +51,9 @@ void testOpenNotRegisteredTenant() { @Test void verifyTestProject() { List tenants = createTenants(); + waitForTenantsProvisioning(tenants); testProject.publish(); - waitForTenantsProvisioning(tenants); verifyTenants(tenants); } diff --git a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/OrderedTestSuite.java b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/OrderedTestSuite.java index c231c1fb285..e46d0de9065 100644 --- a/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/OrderedTestSuite.java +++ b/tests/tests-integrations/src/test/java/org/eclipse/dirigible/integration/tests/ui/tests/OrderedTestSuite.java @@ -9,14 +9,13 @@ */ package org.eclipse.dirigible.integration.tests.ui.tests; -import org.eclipse.dirigible.integration.tests.api.rest.DisabledMultitenantModeIT; import org.junit.platform.suite.api.SelectClasses; import org.junit.platform.suite.api.Suite; import org.junit.platform.suite.api.SuiteDisplayName; @Suite @SuiteDisplayName("Ordered Test Suite") -@SelectClasses({DisabledMultitenantModeIT.class, MultitenancyIT.class}) +@SelectClasses({BPMStarterTemplateIT.class, MultitenancyIT.class}) public class OrderedTestSuite { // use this suite class to run tests in specific order if needed // it is not configured to be executed automatically by the maven plugins diff --git a/tests/tests-integrations/src/test/resources/logback-test.xml b/tests/tests-integrations/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..6d33719e1dc --- /dev/null +++ b/tests/tests-integrations/src/test/resources/logback-test.xml @@ -0,0 +1,75 @@ + + + + + + + + + true + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread{8}] [%tenant] %logger{36} - %msg%n + + + + + ${LOGS_DIR}/dirigible-core-${date}.log + + %date %level [%thread] [%tenant] %logger{10} [%file:%line] %msg%n + + + + + ${LOGS_DIR}/dirigible-apps-${date}.log + + %date %level [%thread] [%tenant] %logger{10} [%file:%line] %msg%n + + + + + ${LOGS_DIR}/dirigible-base-${date}.log + + %date %level [%thread] [%tenant] %logger{10} [%file:%line] %msg%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + +