[KEYCLOAK-14264] - Temporary multi-database support

This commit is contained in:
Pedro Igor 2020-05-20 11:33:20 -03:00 committed by Stian Thorgersen
parent 8c7b69fc9e
commit cc776204f0
6 changed files with 1086 additions and 35 deletions

View file

@ -6,13 +6,16 @@ import javax.persistence.spi.PersistenceUnitTransactionType;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
import org.keycloak.runtime.KeycloakRecorder;
import org.keycloak.connections.jpa.DelegatingDialect;
import io.quarkus.arc.deployment.BeanContainerListenerBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem;
import org.keycloak.runtime.KeycloakRecorder;
class KeycloakProcessor {
@ -24,9 +27,17 @@ class KeycloakProcessor {
@Record(ExecutionTime.STATIC_INIT)
@BuildStep
void configureHibernate(KeycloakRecorder recorder, List<PersistenceUnitDescriptorBuildItem> descriptors) {
// TODO: ORM extension is going to provide build items that we can rely on to create our own PU instead of relying
// on the parsed descriptor and assume that the order that build steps are executed is always the same (although dialect
// is only created during runtime)
ParsedPersistenceXmlDescriptor unit = descriptors.get(0).getDescriptor();
unit.setTransactionType(PersistenceUnitTransactionType.JTA);
unit.getProperties().setProperty(AvailableSettings.DIALECT,
KeycloakRecorder.CONFIG.getRawValue("quarkus.datasource.dialect"));
unit.getProperties().setProperty(AvailableSettings.DIALECT, DelegatingDialect.class.getName());
}
@Record(ExecutionTime.STATIC_INIT)
@BuildStep
void configureDataSource(KeycloakRecorder recorder, BuildProducer<BeanContainerListenerBuildItem> container) {
container.produce(new BeanContainerListenerBuildItem(recorder.configureDataSource()));
}
}

View file

@ -31,11 +31,11 @@
<packaging>pom</packaging>
<properties>
<quarkus.version>1.4.2.Final</quarkus.version>
<resteasy.version>4.4.2.Final</resteasy.version>
<quarkus.version>999-SNAPSHOT</quarkus.version>
<resteasy.version>4.5.3.Final</resteasy.version>
<jackson.version>2.10.2</jackson.version>
<jackson.databind.version>${jackson.version}</jackson.databind.version>
<hibernate.version>5.4.14.Final</hibernate.version>
<hibernate.version>5.4.16.Final</hibernate.version>
<snakeyaml.version>1.20</snakeyaml.version>
<surefire-plugin.version>2.22.0</surefire-plugin.version>

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,9 @@ import liquibase.database.Database;
import liquibase.logging.Logger;
import liquibase.servicelocator.DefaultPackageScanClassResolver;
import liquibase.servicelocator.ServiceLocator;
import org.keycloak.connections.jpa.updater.liquibase.PostgresPlusDatabase;
import org.keycloak.connections.jpa.updater.liquibase.UpdatedMariaDBDatabase;
import org.keycloak.connections.jpa.updater.liquibase.UpdatedMySqlDatabase;
public class FastServiceLocator extends ServiceLocator {

View file

@ -75,52 +75,50 @@ public class QuarkusLiquibaseConnectionProvider implements LiquibaseConnectionPr
protected void baseLiquibaseInitialization(KeycloakSession session) {
resourceAccessor = new ClassLoaderResourceAccessor(getClass().getClassLoader());
FastServiceLocator locator = (FastServiceLocator) ServiceLocator.getInstance();
JpaConnectionProviderFactory jpaConnectionProvider = (JpaConnectionProviderFactory) session
.getKeycloakSessionFactory().getProviderFactory(JpaConnectionProvider.class);
// register our custom databases
locator.register(new PostgresPlusDatabase());
locator.register(new UpdatedMySqlDatabase());
locator.register(new UpdatedMariaDBDatabase());
// registers only the database we are using
try (Connection connection = jpaConnectionProvider.getConnection()) {
Database database = DatabaseFactory.getInstance()
.findCorrectDatabaseImplementation(new JdbcConnection(connection));
DatabaseFactory.getInstance().clearRegistry();
if (database.getDatabaseProductName().equals(PostgresDatabase.PRODUCT_NAME)) {
// Adding PostgresPlus support to liquibase
locator.register(new PostgresPlusDatabase());
} else if (database.getDatabaseProductName().equals(MySQLDatabase.PRODUCT_NAME)) {
// Adding newer version of MySQL/MariaDB support to liquibase
locator.register(new UpdatedMySqlDatabase());
if (database.getDatabaseProductName().equals(MySQLDatabase.PRODUCT_NAME)) {
// Adding CustomVarcharType for MySQL 8 and newer
DataTypeFactory.getInstance().register(MySQL8VarcharType.class);
} else if (database.getDatabaseProductName().equals(MariaDBDatabase.PRODUCT_NAME)) {
locator.register(new UpdatedMariaDBDatabase());
// Adding CustomVarcharType for MySQL 8 and newer
DataTypeFactory.getInstance().register(MySQL8VarcharType.class);
} else {
locator.register(database);
}
// disables XML validation
for (ChangeLogParser parser : ChangeLogParserFactory.getInstance().getParsers()) {
if (parser instanceof XMLChangeLogSAXParser) {
Method getSaxParserFactory = null;
try {
getSaxParserFactory = XMLChangeLogSAXParser.class.getDeclaredMethod("getSaxParserFactory");
getSaxParserFactory.setAccessible(true);
SAXParserFactory saxParserFactory = (SAXParserFactory) getSaxParserFactory.invoke(parser);
saxParserFactory.setValidating(false);
saxParserFactory.setSchema(null);
} catch (Exception e) {
logger.warnf("Failed to disable liquibase XML validations");
} finally {
if (getSaxParserFactory != null) {
getSaxParserFactory.setAccessible(false);
}
DatabaseFactory.getInstance().clearRegistry();
locator.register(database);
} catch (Exception cause) {
throw new RuntimeException("Failed to configure Liquibase database", cause);
}
// disables XML validation
for (ChangeLogParser parser : ChangeLogParserFactory.getInstance().getParsers()) {
if (parser instanceof XMLChangeLogSAXParser) {
Method getSaxParserFactory = null;
try {
getSaxParserFactory = XMLChangeLogSAXParser.class.getDeclaredMethod("getSaxParserFactory");
getSaxParserFactory.setAccessible(true);
SAXParserFactory saxParserFactory = (SAXParserFactory) getSaxParserFactory.invoke(parser);
saxParserFactory.setValidating(false);
saxParserFactory.setSchema(null);
} catch (Exception e) {
logger.warnf("Failed to disable liquibase XML validations");
} finally {
if (getSaxParserFactory != null) {
getSaxParserFactory.setAccessible(false);
}
}
}
} catch (Exception cause) {
throw new RuntimeException("Failed to configure Liquibase database", cause);
}
}

View file

@ -3,6 +3,10 @@ package org.keycloak.runtime;
import java.util.List;
import java.util.Map;
import io.quarkus.agroal.runtime.DataSourceSupport;
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.arc.runtime.BeanContainerListener;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import org.keycloak.connections.liquibase.FastServiceLocator;
import org.keycloak.connections.liquibase.KeycloakLogger;
@ -21,6 +25,10 @@ public class KeycloakRecorder {
CONFIG = (SmallRyeConfig) SmallRyeConfigProviderResolver.instance().getConfig();
}
public static String getDatabaseDialect() {
return CONFIG.getRawValue("quarkus.datasource.dialect");
}
public void configureLiquibase(Map<String, List<String>> services) {
LogFactory.setInstance(new LogFactory() {
KeycloakLogger logger = new KeycloakLogger();
@ -37,4 +45,16 @@ public class KeycloakRecorder {
});
ServiceLocator.setInstance(new FastServiceLocator(services));
}
public BeanContainerListener configureDataSource() {
return new BeanContainerListener() {
@Override
public void created(BeanContainer container) {
String driver = CONFIG.getRawValue("quarkus.datasource.driver");
DataSourceSupport instance = container.instance(DataSourceSupport.class);
DataSourceSupport.Entry entry = instance.entries.get(DataSourceUtil.DEFAULT_DATASOURCE_NAME);
entry.resolvedDriverClass = driver;
}
};
}
}