diff --git a/.github/workflows/ci-x.yml b/.github/workflows/ci-x.yml index 9a6a9300948..a73b7ff8fd6 100644 --- a/.github/workflows/ci-x.yml +++ b/.github/workflows/ci-x.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 1.8 - name: Cache Maven packages uses: actions/cache@v2 with: @@ -38,7 +38,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 1.8 - name: Download Maven Repo uses: actions/download-artifact@v1 with: @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 1.8 - name: Download Maven Repo uses: actions/download-artifact@v1 with: diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/web.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/web.xml index 5ae73fd33a1..5b8d4a61db0 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/web.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/web.xml @@ -49,7 +49,7 @@ Client Connection Filter - org.keycloak.provider.wildfly.WildFlyClientConnectionServletFilter + org.keycloak.provider.wildfly.WildFlyRequestFilter true diff --git a/distribution/server-x/assembly.xml b/distribution/server-x/assembly.xml index 2ce052bd133..35b3db1701b 100755 --- a/distribution/server-x/assembly.xml +++ b/distribution/server-x/assembly.xml @@ -69,10 +69,10 @@ - ../../quarkus/server/target - lib/ + ../../quarkus/server/target/lib + lib - keycloak-runner.jar + **/** diff --git a/distribution/server-x/src/main/content/bin/kc.sh b/distribution/server-x/src/main/content/bin/kc.sh index beb189dff68..fd05fa46160 100644 --- a/distribution/server-x/src/main/content/bin/kc.sh +++ b/distribution/server-x/src/main/content/bin/kc.sh @@ -28,6 +28,8 @@ SERVER_OPTS="-Dkeycloak.home.dir=$DIRNAME/../ -Djboss.server.config.dir=$DIRNAME DEBUG_MODE="${DEBUG:-false}" DEBUG_PORT="${DEBUG_PORT:-8787}" +IS_CONFIGURE="false" + while [ "$#" -gt 0 ] do case "$1" in @@ -38,10 +40,13 @@ do shift fi ;; - --config) + --config-file) SERVER_OPTS="$SERVER_OPTS -Dkeycloak.config.file=$2" shift ;; + config) + IS_CONFIGURE=true + ;; --) shift break;; @@ -71,6 +76,11 @@ if [ "$DEBUG_MODE" = "true" ]; then fi fi -CLASSPATH_OPTS="$DIRNAME/../providers/*:$DIRNAME/../lib/keycloak-runner.jar" +CLASSPATH_OPTS="$DIRNAME/../lib/quarkus-run.jar:$DIRNAME/../lib/main/*" -exec java $JAVA_OPTS $SERVER_OPTS -cp $CLASSPATH_OPTS io.quarkus.runner.GeneratedMain "$@" \ No newline at end of file +if [ "$IS_CONFIGURE" = true ] ; then + echo "Updating the configuration and installing your custom providers, if any. Please wait." + exec java -Dquarkus.launch.rebuild=true $JAVA_OPTS $SERVER_OPTS -cp $CLASSPATH_OPTS io.quarkus.bootstrap.runner.QuarkusEntryPoint "$@" +else + exec java $JAVA_OPTS $SERVER_OPTS -cp $CLASSPATH_OPTS io.quarkus.bootstrap.runner.QuarkusEntryPoint "$@" +fi \ No newline at end of file diff --git a/distribution/server-x/src/main/content/conf/keycloak.properties b/distribution/server-x/src/main/content/conf/keycloak.properties index 25b2649fedc..2543019ac16 100644 --- a/distribution/server-x/src/main/content/conf/keycloak.properties +++ b/distribution/server-x/src/main/content/conf/keycloak.properties @@ -1,5 +1,5 @@ -# Datasource -datasource.dialect=org.hibernate.dialect.H2Dialect +# Default Non-Production Grade Datasource +hibernate-orm.dialect=org.hibernate.dialect.H2Dialect datasource.driver=org.h2.jdbcx.JdbcDataSource datasource.url = jdbc:h2:file:${keycloak.home.dir}/data/keycloakdb;;AUTO_SERVER=TRUE datasource.username = sa diff --git a/distribution/server-x/src/main/content/providers/README.md b/distribution/server-x/src/main/content/providers/README.md new file mode 100644 index 00000000000..b883adfad37 --- /dev/null +++ b/distribution/server-x/src/main/content/providers/README.md @@ -0,0 +1,9 @@ +# Installing Custom Providers + +You should add to this directory your custom provider JAR files. + +Once you have your providers in this directory you should run the following command to complete the installation: + +``` +${keycloak.home.dir}/bin/kc.sh config +``` \ No newline at end of file diff --git a/distribution/server-x/src/main/content/providers/README.txt b/distribution/server-x/src/main/content/providers/README.txt deleted file mode 100644 index 088654c3ed1..00000000000 --- a/distribution/server-x/src/main/content/providers/README.txt +++ /dev/null @@ -1 +0,0 @@ -Copy custom providers into this directory then restart the Keycloak server. diff --git a/quarkus/deployment/pom.xml b/quarkus/deployment/pom.xml index 92a0e64bee4..4493cb801c1 100644 --- a/quarkus/deployment/pom.xml +++ b/quarkus/deployment/pom.xml @@ -24,6 +24,11 @@ quarkus-arc-deployment ${quarkus.version} + + io.quarkus + quarkus-vertx-web-deployment + ${quarkus.version} + org.keycloak keycloak-quarkus-server diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/BuildClassLoader.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/BuildClassLoader.java new file mode 100644 index 00000000000..22ad7defd92 --- /dev/null +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/BuildClassLoader.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.quarkus.deployment; + +import java.io.File; +import java.io.FilenameFilter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +import org.jboss.logging.Logger; + +public class BuildClassLoader extends URLClassLoader { + + private static final Logger logger = Logger.getLogger(BuildClassLoader.class); + + public BuildClassLoader() { + super(new URL[] {}, Thread.currentThread().getContextClassLoader()); + String homeDir = System.getProperty("keycloak.home.dir"); + + if (homeDir == null) { + return; + } + + File providersDir = new File(homeDir + File.separator + "providers"); + + if (providersDir.isDirectory()) { + for (File file : providersDir.listFiles(new JarFilter())) { + try { + addURL(file.toURI().toURL()); + logger.debug("Loading providers from " + file.getAbsolutePath()); + } catch (MalformedURLException e) { + throw new RuntimeException("Failed to add provider JAR at " + file.getAbsolutePath()); + } + } + } + } + + class JarFilter implements FilenameFilter { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".jar"); + } + + } +} diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index 5fa1e3bb96c..53b77c58151 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -1,5 +1,6 @@ package org.keycloak.quarkus.deployment; +import javax.persistence.spi.PersistenceUnitTransactionType; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -7,24 +8,21 @@ import java.util.List; import java.util.Map; import java.util.Set; -import javax.persistence.spi.PersistenceUnitTransactionType; - +import io.quarkus.hibernate.orm.deployment.HibernateOrmConfig; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor; +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; import org.keycloak.Config; import org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory; -import org.keycloak.connections.jpa.DelegatingDialect; import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProviderFactory; import org.keycloak.connections.jpa.updater.liquibase.conn.DefaultLiquibaseConnectionProvider; import org.keycloak.provider.KeycloakDeploymentInfo; import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderManager; import org.keycloak.provider.Spi; -import org.keycloak.provider.quarkus.QuarkusClientConnectionFilter; +import org.keycloak.provider.quarkus.QuarkusRequestFilter; import org.keycloak.runtime.KeycloakRecorder; import org.keycloak.transaction.JBossJtaTransactionManagerLookup; -import io.quarkus.arc.deployment.BeanContainerListenerBuildItem; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; @@ -32,6 +30,7 @@ import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; import io.quarkus.vertx.http.deployment.FilterBuildItem; +import org.keycloak.util.Environment; class KeycloakProcessor { @@ -40,41 +39,50 @@ class KeycloakProcessor { return new FeatureBuildItem("keycloak"); } - @Record(ExecutionTime.STATIC_INIT) - @BuildStep - void configureHibernate(KeycloakRecorder recorder, List 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, DelegatingDialect.class.getName()); - unit.getProperties().setProperty(AvailableSettings.QUERY_STARTUP_CHECKING, Boolean.FALSE.toString()); - } - - @Record(ExecutionTime.STATIC_INIT) - @BuildStep - void configureDataSource(KeycloakRecorder recorder, BuildProducer container) { - container.produce(new BeanContainerListenerBuildItem(recorder.configureDataSource())); - } - /** - *

- * Load the built-in provider factories during build time so we don't spend time looking up them at runtime. + *

Configures the persistence unit for Quarkus. * - *

- * User-defined providers are going to be loaded at startup - *

+ *

The main reason we have this build step is because we re-use the same persistence unit from {@code keycloak-model-jpa} + * module, the same used by the Wildfly distribution. The {@code hibernate-orm} extension expects that the dialect is statically + * set to the persistence unit if there is any from the classpath and use this method to obtain the dialect from the configuration + * file so that we can re-augment the application with whatever dialect we want. In addition to the dialect, we should also be + * allowed to set any additional defaults that we think that makes sense. + * + * @param recorder + * @param config + * @param descriptors */ @Record(ExecutionTime.STATIC_INIT) @BuildStep - void configureBuiltInProviders(KeycloakRecorder recorder) { - recorder.configSessionFactory(loadBuiltInFactories()); + void configureHibernate(KeycloakRecorder recorder, HibernateOrmConfig config, List descriptors) { + PersistenceUnitDescriptor unit = descriptors.get(0).asOutputPersistenceUnitDefinition().getActualHibernateDescriptor(); + + unit.getProperties().setProperty(AvailableSettings.DIALECT, config.dialect.get()); + unit.getProperties().setProperty(AvailableSettings.JPA_TRANSACTION_TYPE, PersistenceUnitTransactionType.JTA.name()); + unit.getProperties().setProperty(AvailableSettings.QUERY_STARTUP_CHECKING, Boolean.FALSE.toString()); } - private Map>> loadBuiltInFactories() { + /** + *

Load the built-in provider factories during build time so we don't spend time looking up them at runtime. + * + *

User-defined providers are going to be loaded at startup

+ * + * @param recorder + */ + @Record(ExecutionTime.STATIC_INIT) + @BuildStep + void configureProviders(KeycloakRecorder recorder) { + recorder.configSessionFactory(loadFactories(), Environment.isRebuild()); + } + + @BuildStep + void initializeRouter(BuildProducer routes) { + routes.produce(new FilterBuildItem(new QuarkusRequestFilter(), FilterBuildItem.AUTHORIZATION - 10)); + } + + private Map>> loadFactories() { ProviderManager pm = new ProviderManager( - KeycloakDeploymentInfo.create().services(), Thread.currentThread().getContextClassLoader(), + KeycloakDeploymentInfo.create().services(), new BuildClassLoader(), Config.scope().getArray("providers")); Map>> result = new HashMap<>(); @@ -98,9 +106,4 @@ class KeycloakProcessor { return result; } - - @BuildStep - void initializeRouter(BuildProducer routes) { - routes.produce(new FilterBuildItem(new QuarkusClientConnectionFilter(), FilterBuildItem.AUTHORIZATION - 10)); - } } diff --git a/quarkus/deployment/src/test/resources/application.properties b/quarkus/deployment/src/test/resources/application.properties index 60642b36146..9396728c7d2 100644 --- a/quarkus/deployment/src/test/resources/application.properties +++ b/quarkus/deployment/src/test/resources/application.properties @@ -1,4 +1,3 @@ -quarkus.log.level = INFO - +quarkus.http.root-path=/auth quarkus.application.name=Keycloak -resteasy.disable.html.sanitizer = true \ No newline at end of file +quarkus.banner.enabled=false \ No newline at end of file diff --git a/quarkus/deployment/src/test/resources/keycloak.properties b/quarkus/deployment/src/test/resources/keycloak.properties index 48bae47916c..24844603e5b 100644 --- a/quarkus/deployment/src/test/resources/keycloak.properties +++ b/quarkus/deployment/src/test/resources/keycloak.properties @@ -1,7 +1,7 @@ hostname.default.frontendUrl = ${keycloak.frontendUrl:} # Datasource -datasource.dialect=org.hibernate.dialect.H2Dialect +hibernate-orm.dialect=org.hibernate.dialect.H2Dialect datasource.jdbc.transactions=xa datasource.driver=org.h2.jdbcx.JdbcDataSource datasource.url = jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 diff --git a/quarkus/pom.xml b/quarkus/pom.xml index f76183f1b1e..f3e555e85b1 100755 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -31,7 +31,7 @@ pom - 1.6.0.CR1 + 999-SNAPSHOT 4.5.5.Final 2.10.4 ${jackson.version} @@ -137,5 +137,33 @@ runtime server + + + + + dependency-snapshots-repo + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + + + + + dependency-snapshots-repo + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + diff --git a/quarkus/runtime/src/main/java/org/keycloak/QuarkusKeycloakSessionFactory.java b/quarkus/runtime/src/main/java/org/keycloak/QuarkusKeycloakSessionFactory.java index bcf97d8b0f1..16abe0ec3fb 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/QuarkusKeycloakSessionFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/QuarkusKeycloakSessionFactory.java @@ -1,5 +1,6 @@ package org.keycloak; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -13,7 +14,6 @@ import org.jboss.logging.Logger; import org.keycloak.provider.KeycloakDeploymentInfo; import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderLoader; -import org.keycloak.provider.ProviderManager; import org.keycloak.provider.ProviderManagerRegistry; import org.keycloak.provider.Spi; import org.keycloak.services.DefaultKeycloakSessionFactory; @@ -37,23 +37,25 @@ public final class QuarkusKeycloakSessionFactory extends DefaultKeycloakSessionF } private static QuarkusKeycloakSessionFactory INSTANCE; - private Map>> factories; + private final Boolean reaugmented; + private final Map>> factories; - public QuarkusKeycloakSessionFactory(Map>> factories) { + public QuarkusKeycloakSessionFactory(Map>> factories, Boolean reaugmented) { this.factories = factories; + this.reaugmented = reaugmented; } private QuarkusKeycloakSessionFactory() { + reaugmented = false; + factories = Collections.emptyMap(); } @Override public void init() { serverStartupTimestamp = System.currentTimeMillis(); - ProviderLoader userProviderLoader = createUserProviderLoader(); - spis = loadRuntimeSpis(userProviderLoader); + spis = factories.keySet(); for (Spi spi : spis) { - loadUserProviders(spi, userProviderLoader); for (Class factoryClazz : factories.get(spi)) { ProviderFactory factory = lookupProviderFactory(factoryClazz); Config.Scope scope = Config.scope(spi.getName(), factory.getId()); @@ -86,27 +88,6 @@ public final class QuarkusKeycloakSessionFactory extends DefaultKeycloakSessionF ProviderManagerRegistry.SINGLETON.setDeployer(this); } - private Set loadRuntimeSpis(ProviderLoader runtimeLoader) { - // most of the time SPIs loaded at build time are enough but under certain circumstances (e.g.: testsuite) we may - // want to load additional SPIs at runtime only from the JARs deployed at the providers dir - List loaded = runtimeLoader.loadSpis(); - - if (loaded.isEmpty()) { - return factories.keySet(); - } - - Set spis = new HashSet<>(factories.keySet()); - - spis.addAll(loaded); - - return spis; - } - - private ProviderLoader createUserProviderLoader() { - return UserProviderLoader - .create(KeycloakDeploymentInfo.create().services(), Thread.currentThread().getContextClassLoader()); - } - private ProviderFactory lookupProviderFactory(Class factoryClazz) { ProviderFactory factory; @@ -154,19 +135,4 @@ public final class QuarkusKeycloakSessionFactory extends DefaultKeycloakSessionF logger.debugv("No default provider for {0}", spi.getName()); } } - - private void loadUserProviders(Spi spi, ProviderLoader loader) { - //TODO: support loading providers from CDI. We should probably consider writing providers using CDI for Quarkus, much easier - // to develop and integrate with - List load = loader.load(spi); - - for (ProviderFactory factory : load) { - factories.computeIfAbsent(spi, new Function>>() { - @Override - public Set> apply(Spi spi) { - return new HashSet<>(); - } - }).add(factory.getClass()); - } - } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/UserProviderLoader.java b/quarkus/runtime/src/main/java/org/keycloak/UserProviderLoader.java deleted file mode 100644 index 93f417c55ef..00000000000 --- a/quarkus/runtime/src/main/java/org/keycloak/UserProviderLoader.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.keycloak; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.LinkedList; -import java.util.List; - -import org.jboss.logging.Logger; -import org.keycloak.provider.DefaultProviderLoader; -import org.keycloak.provider.KeycloakDeploymentInfo; -import org.keycloak.provider.ProviderLoader; - -class UserProviderLoader { - - private static final Logger logger = Logger.getLogger(UserProviderLoader.class); - - static ProviderLoader create(KeycloakDeploymentInfo info, ClassLoader parentClassLoader) { - return new DefaultProviderLoader(info, createClassLoader(parentClassLoader)); - } - - private static ClassLoader createClassLoader(ClassLoader parent) { - String homeDir = System.getProperty("keycloak.home.dir"); - - if (homeDir == null) { - // don't load resources from classpath - return new ClassLoader() { - @Override - public Enumeration getResources(String name) throws IOException { - return Collections.emptyEnumeration(); - } - }; - } - - try { - List urls = new LinkedList(); - File dir = new File(homeDir + File.separator + "providers"); - - if (dir.isDirectory()) { - for (File file : dir.listFiles(new JarFilter())) { - urls.add(file.toURI().toURL()); - } - } - - logger.debug("Loading providers from " + urls.toString()); - - return new URLClassLoader(urls.toArray(new URL[urls.size()]), parent) { - @Override - public Enumeration getResources(String name) throws IOException { - Enumeration resources = findResources(name); - List result = new ArrayList<>(); - - while (resources.hasMoreElements()) { - URL url = resources.nextElement(); - - if (url.toString().contains(dir.getAbsolutePath())) { - result.add(url); - } - } - - return Collections.enumeration(result); - } - }; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static class JarFilter implements FilenameFilter { - - @Override - public boolean accept(File dir, String name) { - return name.toLowerCase().endsWith(".jar"); - } - - } -} diff --git a/quarkus/runtime/src/main/java/org/keycloak/connections/jpa/DelegatingDialect.java b/quarkus/runtime/src/main/java/org/keycloak/connections/jpa/DelegatingDialect.java deleted file mode 100644 index 10eb768fac3..00000000000 --- a/quarkus/runtime/src/main/java/org/keycloak/connections/jpa/DelegatingDialect.java +++ /dev/null @@ -1,1019 +0,0 @@ -package org.keycloak.connections.jpa; - -import java.sql.CallableStatement; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.HibernateException; -import org.hibernate.LockMode; -import org.hibernate.LockOptions; -import org.hibernate.MappingException; -import org.hibernate.NullPrecedence; -import org.hibernate.ScrollMode; -import org.hibernate.boot.model.TypeContributions; -import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; -import org.hibernate.boot.model.relational.Sequence; -import org.hibernate.dialect.ColumnAliasExtractor; -import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.LobMergeStrategy; -import org.hibernate.dialect.identity.IdentityColumnSupport; -import org.hibernate.dialect.lock.LockingStrategy; -import org.hibernate.dialect.pagination.LimitHandler; -import org.hibernate.dialect.unique.UniqueDelegate; -import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; -import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; -import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; -import org.hibernate.engine.jdbc.env.spi.SchemaNameResolver; -import org.hibernate.engine.spi.QueryParameters; -import org.hibernate.exception.spi.SQLExceptionConversionDelegate; -import org.hibernate.exception.spi.SQLExceptionConverter; -import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; -import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy; -import org.hibernate.internal.SessionFactoryImpl; -import org.hibernate.loader.BatchLoadSizingStrategy; -import org.hibernate.mapping.Constraint; -import org.hibernate.mapping.ForeignKey; -import org.hibernate.mapping.Index; -import org.hibernate.mapping.Table; -import org.hibernate.persister.entity.Lockable; -import org.hibernate.procedure.spi.CallableStatementSupport; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.sql.CaseFragment; -import org.hibernate.sql.JoinFragment; -import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; -import org.hibernate.tool.schema.spi.Exporter; -import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; -import org.keycloak.runtime.KeycloakRecorder; - -/** - * Temporary solution for multiple database support on Quarkus until we get a capability from Quarkus to re-build the application - */ -public class DelegatingDialect extends Dialect { - - private Dialect dialect; - - private Dialect getInstance() { - if (dialect == null) { - // dialect is initialized during startup when hibernate is booting - String dialectClazz = KeycloakRecorder.getDatabaseDialect(); - - try { - dialect = (Dialect) Class.forName(dialectClazz).getDeclaredConstructor().newInstance(); - } catch (Exception cause) { - throw new RuntimeException("Failed to create dialect instance for [" + dialectClazz + "]", cause); - } - } - - return dialect; - } - - @Override - public void contributeTypes(TypeContributions typeContributions, - ServiceRegistry serviceRegistry) { - getInstance().contributeTypes(typeContributions, serviceRegistry); - } - - @Override - public String getTypeName(int code) throws HibernateException { - return getInstance().getTypeName(code); - } - - @Override - public String getTypeName(int code, long length, int precision, int scale) throws HibernateException { - return getInstance().getTypeName(code, length, precision, scale); - } - - @Override - public String getCastTypeName(int code) { - return getInstance().getCastTypeName(code); - } - - @Override - public String cast(String value, int jdbcTypeCode, int length, int precision, int scale) { - return getInstance().cast(value, jdbcTypeCode, length, precision, scale); - } - - @Override - public String cast(String value, int jdbcTypeCode, int length) { - return getInstance().cast(value, jdbcTypeCode, length); - } - - @Override - public String cast(String value, int jdbcTypeCode, int precision, int scale) { - return getInstance().cast(value, jdbcTypeCode, precision, scale); - } - - @Override - public SqlTypeDescriptor remapSqlTypeDescriptor( - SqlTypeDescriptor sqlTypeDescriptor) { - return getInstance().remapSqlTypeDescriptor(sqlTypeDescriptor); - } - - @Override - public LobMergeStrategy getLobMergeStrategy() { - return getInstance().getLobMergeStrategy(); - } - - @Override - public String getHibernateTypeName(int code) throws HibernateException { - return getInstance().getHibernateTypeName(code); - } - - @Override - public boolean isTypeNameRegistered(String typeName) { - return getInstance().isTypeNameRegistered(typeName); - } - - @Override - public String getHibernateTypeName(int code, int length, int precision, int scale) throws HibernateException { - return getInstance().getHibernateTypeName(code, length, precision, scale); - } - - @Override - @Deprecated - public Class getNativeIdentifierGeneratorClass() { - return getInstance().getNativeIdentifierGeneratorClass(); - } - - @Override - public String getNativeIdentifierGeneratorStrategy() { - return getInstance().getNativeIdentifierGeneratorStrategy(); - } - - @Override - public IdentityColumnSupport getIdentityColumnSupport() { - return getInstance().getIdentityColumnSupport(); - } - - @Override - public boolean supportsSequences() { - return getInstance().supportsSequences(); - } - - @Override - public boolean supportsPooledSequences() { - return getInstance().supportsPooledSequences(); - } - - @Override - public String getSequenceNextValString(String sequenceName) throws MappingException { - return getInstance().getSequenceNextValString(sequenceName); - } - - @Override - public String getSelectSequenceNextValString(String sequenceName) throws MappingException { - return getInstance().getSelectSequenceNextValString(sequenceName); - } - - @Override - @Deprecated - public String[] getCreateSequenceStrings(String sequenceName) throws MappingException { - return getInstance().getCreateSequenceStrings(sequenceName); - } - - @Override - public String[] getCreateSequenceStrings(String sequenceName, int initialValue, int incrementSize) throws MappingException { - return getInstance().getCreateSequenceStrings(sequenceName, initialValue, incrementSize); - } - - @Override - public String[] getDropSequenceStrings(String sequenceName) throws MappingException { - return getInstance().getDropSequenceStrings(sequenceName); - } - - @Override - public String getQuerySequencesString() { - return getInstance().getQuerySequencesString(); - } - - @Override - public SequenceInformationExtractor getSequenceInformationExtractor() { - return getInstance().getSequenceInformationExtractor(); - } - - @Override - public String getSelectGUIDString() { - return getInstance().getSelectGUIDString(); - } - - @Override - public LimitHandler getLimitHandler() { - return getInstance().getLimitHandler(); - } - - @Override - @Deprecated - public boolean supportsLimit() { - return getInstance().supportsLimit(); - } - - @Override - @Deprecated - public boolean supportsLimitOffset() { - return getInstance().supportsLimitOffset(); - } - - @Override - @Deprecated - public boolean supportsVariableLimit() { - return getInstance().supportsVariableLimit(); - } - - @Override - @Deprecated - public boolean bindLimitParametersInReverseOrder() { - return getInstance().bindLimitParametersInReverseOrder(); - } - - @Override - @Deprecated - public boolean bindLimitParametersFirst() { - return getInstance().bindLimitParametersFirst(); - } - - @Override - @Deprecated - public boolean useMaxForLimit() { - return getInstance().useMaxForLimit(); - } - - @Override - @Deprecated - public boolean forceLimitUsage() { - return getInstance().forceLimitUsage(); - } - - @Override - @Deprecated - public String getLimitString(String query, int offset, int limit) { - return getInstance().getLimitString(query, offset, limit); - } - - @Override - @Deprecated - public int convertToFirstRowValue(int zeroBasedFirstResult) { - return getInstance().convertToFirstRowValue(zeroBasedFirstResult); - } - - @Override - public boolean supportsLockTimeouts() { - return getInstance().supportsLockTimeouts(); - } - - @Override - public boolean isLockTimeoutParameterized() { - return getInstance().isLockTimeoutParameterized(); - } - - @Override - public LockingStrategy getLockingStrategy(Lockable lockable, - LockMode lockMode) { - return getInstance().getLockingStrategy(lockable, lockMode); - } - - @Override - public String getForUpdateString(LockOptions lockOptions) { - return getInstance().getForUpdateString(lockOptions); - } - - @Override - public String getForUpdateString(LockMode lockMode) { - return getInstance().getForUpdateString(lockMode); - } - - @Override - public String getForUpdateString() { - return getInstance().getForUpdateString(); - } - - @Override - public String getWriteLockString(int timeout) { - return getInstance().getWriteLockString(timeout); - } - - @Override - public String getWriteLockString(String aliases, int timeout) { - return getInstance().getWriteLockString(aliases, timeout); - } - - @Override - public String getReadLockString(int timeout) { - return getInstance().getReadLockString(timeout); - } - - @Override - public String getReadLockString(String aliases, int timeout) { - return getInstance().getReadLockString(aliases, timeout); - } - - @Override - public boolean forUpdateOfColumns() { - return getInstance().forUpdateOfColumns(); - } - - @Override - public boolean supportsOuterJoinForUpdate() { - return getInstance().supportsOuterJoinForUpdate(); - } - - @Override - public String getForUpdateString(String aliases) { - return getInstance().getForUpdateString(aliases); - } - - @Override - public String getForUpdateString(String aliases, LockOptions lockOptions) { - return getInstance().getForUpdateString(aliases, lockOptions); - } - - @Override - public String getForUpdateNowaitString() { - return getInstance().getForUpdateNowaitString(); - } - - @Override - public String getForUpdateSkipLockedString() { - return getInstance().getForUpdateSkipLockedString(); - } - - @Override - public String getForUpdateNowaitString(String aliases) { - return getInstance().getForUpdateNowaitString(aliases); - } - - @Override - public String getForUpdateSkipLockedString(String aliases) { - return getInstance().getForUpdateSkipLockedString(aliases); - } - - @Override - @Deprecated - public String appendLockHint(LockMode mode, String tableName) { - return getInstance().appendLockHint(mode, tableName); - } - - @Override - public String appendLockHint(LockOptions lockOptions, String tableName) { - return getInstance().appendLockHint(lockOptions, tableName); - } - - @Override - public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, - Map keyColumnNames) { - return getInstance().applyLocksToSql(sql, aliasedLockOptions, keyColumnNames); - } - - @Override - public String getCreateTableString() { - return getInstance().getCreateTableString(); - } - - @Override - public String getAlterTableString(String tableName) { - return getInstance().getAlterTableString(tableName); - } - - @Override - public String getCreateMultisetTableString() { - return getInstance().getCreateMultisetTableString(); - } - - @Override - public MultiTableBulkIdStrategy getDefaultMultiTableBulkIdStrategy() { - return getInstance().getDefaultMultiTableBulkIdStrategy(); - } - - @Override - public int registerResultSetOutParameter(CallableStatement statement, int position) throws SQLException { - return getInstance().registerResultSetOutParameter(statement, position); - } - - @Override - public int registerResultSetOutParameter(CallableStatement statement, String name) throws SQLException { - return getInstance().registerResultSetOutParameter(statement, name); - } - - @Override - public ResultSet getResultSet(CallableStatement statement) throws SQLException { - return getInstance().getResultSet(statement); - } - - @Override - public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException { - return getInstance().getResultSet(statement, position); - } - - @Override - public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException { - return getInstance().getResultSet(statement, name); - } - - @Override - public boolean supportsCurrentTimestampSelection() { - return getInstance().supportsCurrentTimestampSelection(); - } - - @Override - public boolean isCurrentTimestampSelectStringCallable() { - return getInstance().isCurrentTimestampSelectStringCallable(); - } - - @Override - public String getCurrentTimestampSelectString() { - return getInstance().getCurrentTimestampSelectString(); - } - - @Override - public String getCurrentTimestampSQLFunctionName() { - return getInstance().getCurrentTimestampSQLFunctionName(); - } - - @Override - @Deprecated - public SQLExceptionConverter buildSQLExceptionConverter() { - return getInstance().buildSQLExceptionConverter(); - } - - @Override - public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { - return getInstance().buildSQLExceptionConversionDelegate(); - } - - @Override - public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { - return getInstance().getViolatedConstraintNameExtracter(); - } - - @Override - public String getSelectClauseNullString(int sqlType) { - return getInstance().getSelectClauseNullString(sqlType); - } - - @Override - public boolean supportsUnionAll() { - return getInstance().supportsUnionAll(); - } - - @Override - public JoinFragment createOuterJoinFragment() { - return getInstance().createOuterJoinFragment(); - } - - @Override - public CaseFragment createCaseFragment() { - return getInstance().createCaseFragment(); - } - - @Override - public String getNoColumnsInsertString() { - return getInstance().getNoColumnsInsertString(); - } - - @Override - public boolean supportsNoColumnsInsert() { - return getInstance().supportsNoColumnsInsert(); - } - - @Override - public String getLowercaseFunction() { - return getInstance().getLowercaseFunction(); - } - - @Override - public String getCaseInsensitiveLike() { - return getInstance().getCaseInsensitiveLike(); - } - - @Override - public boolean supportsCaseInsensitiveLike() { - return getInstance().supportsCaseInsensitiveLike(); - } - - @Override - public String transformSelectString(String select) { - return getInstance().transformSelectString(select); - } - - @Override - public int getMaxAliasLength() { - return getInstance().getMaxAliasLength(); - } - - @Override - public String toBooleanValueString(boolean bool) { - return getInstance().toBooleanValueString(bool); - } - - @Override - @Deprecated - public Set getKeywords() { - return getInstance().getKeywords(); - } - - @Override - public IdentifierHelper buildIdentifierHelper( - IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) throws SQLException { - return getInstance().buildIdentifierHelper(builder, dbMetaData); - } - - @Override - public char openQuote() { - return getInstance().openQuote(); - } - - @Override - public char closeQuote() { - return getInstance().closeQuote(); - } - - @Override - public Exporter getTableExporter() { - return getInstance().getTableExporter(); - } - - @Override - public Exporter getSequenceExporter() { - return getInstance().getSequenceExporter(); - } - - @Override - public Exporter getIndexExporter() { - return getInstance().getIndexExporter(); - } - - @Override - public Exporter getForeignKeyExporter() { - return getInstance().getForeignKeyExporter(); - } - - @Override - public Exporter getUniqueKeyExporter() { - return getInstance().getUniqueKeyExporter(); - } - - @Override - public Exporter getAuxiliaryDatabaseObjectExporter() { - return getInstance().getAuxiliaryDatabaseObjectExporter(); - } - - @Override - public boolean canCreateCatalog() { - return getInstance().canCreateCatalog(); - } - - @Override - public String[] getCreateCatalogCommand(String catalogName) { - return getInstance().getCreateCatalogCommand(catalogName); - } - - @Override - public String[] getDropCatalogCommand(String catalogName) { - return getInstance().getDropCatalogCommand(catalogName); - } - - @Override - public boolean canCreateSchema() { - return getInstance().canCreateSchema(); - } - - @Override - public String[] getCreateSchemaCommand(String schemaName) { - return getInstance().getCreateSchemaCommand(schemaName); - } - - @Override - public String[] getDropSchemaCommand(String schemaName) { - return getInstance().getDropSchemaCommand(schemaName); - } - - @Override - public String getCurrentSchemaCommand() { - return getInstance().getCurrentSchemaCommand(); - } - - @Override - public SchemaNameResolver getSchemaNameResolver() { - return getInstance().getSchemaNameResolver(); - } - - @Override - public boolean hasAlterTable() { - return getInstance().hasAlterTable(); - } - - @Override - public boolean dropConstraints() { - return getInstance().dropConstraints(); - } - - @Override - public boolean qualifyIndexName() { - return getInstance().qualifyIndexName(); - } - - @Override - public String getAddColumnString() { - return getInstance().getAddColumnString(); - } - - @Override - public String getAddColumnSuffixString() { - return getInstance().getAddColumnSuffixString(); - } - - @Override - public String getDropForeignKeyString() { - return getInstance().getDropForeignKeyString(); - } - - @Override - public String getTableTypeString() { - return getInstance().getTableTypeString(); - } - - @Override - public String getAddForeignKeyConstraintString(String constraintName, String[] foreignKey, String referencedTable, - String[] primaryKey, boolean referencesPrimaryKey) { - return getInstance().getAddForeignKeyConstraintString(constraintName, foreignKey, referencedTable, primaryKey, - referencesPrimaryKey); - } - - @Override - public String getAddForeignKeyConstraintString(String constraintName, String foreignKeyDefinition) { - return getInstance().getAddForeignKeyConstraintString(constraintName, foreignKeyDefinition); - } - - @Override - public String getAddPrimaryKeyConstraintString(String constraintName) { - return getInstance().getAddPrimaryKeyConstraintString(constraintName); - } - - @Override - public boolean hasSelfReferentialForeignKeyBug() { - return getInstance().hasSelfReferentialForeignKeyBug(); - } - - @Override - public String getNullColumnString() { - return getInstance().getNullColumnString(); - } - - @Override - public boolean supportsCommentOn() { - return getInstance().supportsCommentOn(); - } - - @Override - public String getTableComment(String comment) { - return getInstance().getTableComment(comment); - } - - @Override - public String getColumnComment(String comment) { - return getInstance().getColumnComment(comment); - } - - @Override - public boolean supportsIfExistsBeforeTableName() { - return getInstance().supportsIfExistsBeforeTableName(); - } - - @Override - public boolean supportsIfExistsAfterTableName() { - return getInstance().supportsIfExistsAfterTableName(); - } - - @Override - public boolean supportsIfExistsBeforeConstraintName() { - return getInstance().supportsIfExistsBeforeConstraintName(); - } - - @Override - public boolean supportsIfExistsAfterConstraintName() { - return getInstance().supportsIfExistsAfterConstraintName(); - } - - @Override - public boolean supportsIfExistsAfterAlterTable() { - return getInstance().supportsIfExistsAfterAlterTable(); - } - - @Override - public String getDropTableString(String tableName) { - return getInstance().getDropTableString(tableName); - } - - @Override - public boolean supportsColumnCheck() { - return getInstance().supportsColumnCheck(); - } - - @Override - public boolean supportsTableCheck() { - return getInstance().supportsTableCheck(); - } - - @Override - public boolean supportsCascadeDelete() { - return getInstance().supportsCascadeDelete(); - } - - @Override - public String getCascadeConstraintsString() { - return getInstance().getCascadeConstraintsString(); - } - - @Override - public String getCrossJoinSeparator() { - return getInstance().getCrossJoinSeparator(); - } - - @Override - public ColumnAliasExtractor getColumnAliasExtractor() { - return getInstance().getColumnAliasExtractor(); - } - - @Override - public boolean supportsEmptyInList() { - return getInstance().supportsEmptyInList(); - } - - @Override - public boolean areStringComparisonsCaseInsensitive() { - return getInstance().areStringComparisonsCaseInsensitive(); - } - - @Override - public boolean supportsRowValueConstructorSyntax() { - return getInstance().supportsRowValueConstructorSyntax(); - } - - @Override - public boolean supportsRowValueConstructorSyntaxInInList() { - return getInstance().supportsRowValueConstructorSyntaxInInList(); - } - - @Override - public boolean useInputStreamToInsertBlob() { - return getInstance().useInputStreamToInsertBlob(); - } - - @Override - public boolean supportsParametersInInsertSelect() { - return getInstance().supportsParametersInInsertSelect(); - } - - @Override - public boolean replaceResultVariableInOrderByClauseWithPosition() { - return getInstance().replaceResultVariableInOrderByClauseWithPosition(); - } - - @Override - public String renderOrderByElement(String expression, String collation, String order, NullPrecedence nulls) { - return getInstance().renderOrderByElement(expression, collation, order, nulls); - } - - @Override - public boolean requiresCastingOfParametersInSelectClause() { - return getInstance().requiresCastingOfParametersInSelectClause(); - } - - @Override - public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() { - return getInstance().supportsResultSetPositionQueryMethodsOnForwardOnlyCursor(); - } - - @Override - public boolean supportsCircularCascadeDeleteConstraints() { - return getInstance().supportsCircularCascadeDeleteConstraints(); - } - - @Override - public boolean supportsSubselectAsInPredicateLHS() { - return getInstance().supportsSubselectAsInPredicateLHS(); - } - - @Override - public boolean supportsExpectedLobUsagePattern() { - return getInstance().supportsExpectedLobUsagePattern(); - } - - @Override - public boolean supportsLobValueChangePropogation() { - return getInstance().supportsLobValueChangePropogation(); - } - - @Override - public boolean supportsUnboundedLobLocatorMaterialization() { - return getInstance().supportsUnboundedLobLocatorMaterialization(); - } - - @Override - public boolean supportsSubqueryOnMutatingTable() { - return getInstance().supportsSubqueryOnMutatingTable(); - } - - @Override - public boolean supportsExistsInSelect() { - return getInstance().supportsExistsInSelect(); - } - - @Override - public boolean doesReadCommittedCauseWritersToBlockReaders() { - return getInstance().doesReadCommittedCauseWritersToBlockReaders(); - } - - @Override - public boolean doesRepeatableReadCauseReadersToBlockWriters() { - return getInstance().doesRepeatableReadCauseReadersToBlockWriters(); - } - - @Override - public boolean supportsBindAsCallableArgument() { - return getInstance().supportsBindAsCallableArgument(); - } - - @Override - public boolean supportsTupleCounts() { - return getInstance().supportsTupleCounts(); - } - - @Override - public boolean supportsTupleDistinctCounts() { - return getInstance().supportsTupleDistinctCounts(); - } - - @Override - public boolean requiresParensForTupleDistinctCounts() { - return getInstance().requiresParensForTupleDistinctCounts(); - } - - @Override - public int getInExpressionCountLimit() { - return getInstance().getInExpressionCountLimit(); - } - - @Override - public boolean forceLobAsLastValue() { - return getInstance().forceLobAsLastValue(); - } - - @Override - @Deprecated - public boolean useFollowOnLocking() { - return getInstance().useFollowOnLocking(); - } - - @Override - public boolean useFollowOnLocking(QueryParameters parameters) { - return getInstance().useFollowOnLocking(parameters); - } - - @Override - public String getNotExpression(String expression) { - return getInstance().getNotExpression(expression); - } - - @Override - public UniqueDelegate getUniqueDelegate() { - return getInstance().getUniqueDelegate(); - } - - @Override - @Deprecated - public boolean supportsUnique() { - return getInstance().supportsUnique(); - } - - @Override - @Deprecated - public boolean supportsUniqueConstraintInCreateAlterTable() { - return getInstance().supportsUniqueConstraintInCreateAlterTable(); - } - - @Override - @Deprecated - public String getAddUniqueConstraintString(String constraintName) { - return getInstance().getAddUniqueConstraintString(constraintName); - } - - @Override - @Deprecated - public boolean supportsNotNullUnique() { - return getInstance().supportsNotNullUnique(); - } - - @Override - public String getQueryHintString(String query, List hintList) { - return getInstance().getQueryHintString(query, hintList); - } - - @Override - public String getQueryHintString(String query, String hints) { - return getInstance().getQueryHintString(query, hints); - } - - @Override - public ScrollMode defaultScrollMode() { - return getInstance().defaultScrollMode(); - } - - @Override - public boolean supportsTuplesInSubqueries() { - return getInstance().supportsTuplesInSubqueries(); - } - - @Override - public CallableStatementSupport getCallableStatementSupport() { - return getInstance().getCallableStatementSupport(); - } - - @Override - public NameQualifierSupport getNameQualifierSupport() { - return getInstance().getNameQualifierSupport(); - } - - @Override - public BatchLoadSizingStrategy getDefaultBatchLoadSizingStrategy() { - return getInstance().getDefaultBatchLoadSizingStrategy(); - } - - @Override - public boolean isJdbcLogWarningsEnabledByDefault() { - return getInstance().isJdbcLogWarningsEnabledByDefault(); - } - - @Override - public void augmentRecognizedTableTypes(List tableTypesList) { - getInstance().augmentRecognizedTableTypes(tableTypesList); - } - - @Override - public boolean supportsPartitionBy() { - return getInstance().supportsPartitionBy(); - } - - @Override - public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException { - return getInstance().supportsNamedParameters(databaseMetaData); - } - - @Override - public boolean supportsNationalizedTypes() { - return getInstance().supportsNationalizedTypes(); - } - - @Override - public boolean supportsNonQueryWithCTE() { - return getInstance().supportsNonQueryWithCTE(); - } - - @Override - public boolean supportsValuesList() { - return getInstance().supportsValuesList(); - } - - @Override - public boolean supportsSkipLocked() { - return getInstance().supportsSkipLocked(); - } - - @Override - public boolean supportsNoWait() { - return getInstance().supportsNoWait(); - } - - @Override - public boolean isLegacyLimitHandlerBehaviorEnabled() { - return getInstance().isLegacyLimitHandlerBehaviorEnabled(); - } - - @Override - public String inlineLiteral(String literal) { - return getInstance().inlineLiteral(literal); - } - - @Override - public boolean supportsJdbcConnectionLobCreation(DatabaseMetaData databaseMetaData) { - return getInstance().supportsJdbcConnectionLobCreation(databaseMetaData); - } - - @Override - public String addSqlHintOrComment(String sql, QueryParameters parameters, boolean commentsEnabled) { - return getInstance().addSqlHintOrComment(sql, parameters, commentsEnabled); - } - - @Override - public boolean supportsSelectAliasInGroupByClause() { - return getInstance().supportsSelectAliasInGroupByClause(); - } - - @Override - public String toString() { - return getInstance().toString(); - } -} diff --git a/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/KeycloakPropertiesConfigSource.java b/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/KeycloakPropertiesConfigSource.java index ce2cf01978c..23dfb3e65d9 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/KeycloakPropertiesConfigSource.java +++ b/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/KeycloakPropertiesConfigSource.java @@ -125,6 +125,13 @@ public abstract class KeycloakPropertiesConfigSource extends PropertiesConfigSou return result; } + /** + * We need a better namespace resolution so that we don't need to add Quarkus extensions manually. Maybe the easiest + * path is to just have the "keycloak" namespace for Keycloak-specific properties. + * + * @param key the key to transform + * @return the same key but prefixed with the namespace + */ private static String transformKey(String key) { String namespace; String[] keyParts = key.split("\\."); @@ -138,6 +145,7 @@ public abstract class KeycloakPropertiesConfigSource extends PropertiesConfigSou } switch (extension) { + case "hibernate-orm": case "datasource": case "http": case "log": diff --git a/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusClientConnectionFilter.java b/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusRequestFilter.java similarity index 79% rename from quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusClientConnectionFilter.java rename to quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusRequestFilter.java index e2d7309e37f..05fa72b2df1 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusClientConnectionFilter.java +++ b/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusRequestFilter.java @@ -17,11 +17,9 @@ package org.keycloak.provider.quarkus; -import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters; -import org.jboss.resteasy.spi.ResteasyDeployment; import org.keycloak.common.ClientConnection; -import org.keycloak.common.util.Resteasy; -import org.keycloak.services.filters.AbstractClientConnectionFilter; +import org.keycloak.models.KeycloakSession; +import org.keycloak.services.filters.AbstractRequestFilter; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; @@ -35,8 +33,9 @@ import io.vertx.ext.web.RoutingContext; *

The filter itself runs in a event loop and should delegate to worker threads any blocking code (for now, all requests are handled * as blocking). */ -public class QuarkusClientConnectionFilter extends AbstractClientConnectionFilter implements Handler { +public class QuarkusRequestFilter extends AbstractRequestFilter implements Handler { + private static final String KEYCLOAK_SESSION_KEY = KeycloakSession.class.getName(); private static final Handler> EMPTY_RESULT = result -> { // we don't really care about the result because any exception thrown should be handled by the parent class }; @@ -49,6 +48,10 @@ public class QuarkusClientConnectionFilter extends AbstractClientConnectionFilte // in the event loop context.vertx().executeBlocking(promise -> filter(clientConnection, (session) -> { try { + // we need to close the session before response is sent to the client, otherwise subsequent requests could + // not get the latest state because the session from the previous request is still being closed + // other methods from Vert.x to add a handler to the response works asynchronously + context.response().headersEndHandler(event -> close(session)); context.next(); promise.complete(); } catch (Exception cause) { @@ -59,6 +62,11 @@ public class QuarkusClientConnectionFilter extends AbstractClientConnectionFilte }), EMPTY_RESULT); } + @Override + protected boolean isAutoClose() { + return false; + } + private ClientConnection createClientConnection(HttpServerRequest request) { return new ClientConnection() { @Override diff --git a/quarkus/runtime/src/main/java/org/keycloak/runtime/KeycloakRecorder.java b/quarkus/runtime/src/main/java/org/keycloak/runtime/KeycloakRecorder.java index 3621e4b0908..ead62ad828b 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/runtime/KeycloakRecorder.java +++ b/quarkus/runtime/src/main/java/org/keycloak/runtime/KeycloakRecorder.java @@ -29,10 +29,6 @@ public class KeycloakRecorder { CONFIG = (SmallRyeConfig) SmallRyeConfigProviderResolver.instance().getConfig(); } - public static String getDatabaseDialect() { - return CONFIG.getRawValue("quarkus.datasource.dialect"); - } - public void configureLiquibase(Map> services) { LogFactory.setInstance(new LogFactory() { KeycloakLogger logger = new KeycloakLogger(); @@ -57,19 +53,7 @@ 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; - } - }; - } - - public void configSessionFactory(Map>> factories) { - QuarkusKeycloakSessionFactory.setInstance(new QuarkusKeycloakSessionFactory(factories)); + public void configSessionFactory(Map>> factories, Boolean reaugmented) { + QuarkusKeycloakSessionFactory.setInstance(new QuarkusKeycloakSessionFactory(factories, reaugmented)); } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java b/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java new file mode 100644 index 00000000000..5cafccafdf0 --- /dev/null +++ b/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.util; + +public final class Environment { + + public static Boolean isRebuild() { + return Boolean.valueOf(System.getProperty("quarkus.launch.rebuild")); + } +} diff --git a/quarkus/server/pom.xml b/quarkus/server/pom.xml index cf09fa11c50..5e298a9bf83 100644 --- a/quarkus/server/pom.xml +++ b/quarkus/server/pom.xml @@ -63,7 +63,6 @@ quarkus-maven-plugin ${quarkus.version} - true keycloak diff --git a/quarkus/server/src/main/resources/META-INF/keycloak.properties b/quarkus/server/src/main/resources/META-INF/keycloak.properties index 84da3797fa1..aa4a3c9de0e 100644 --- a/quarkus/server/src/main/resources/META-INF/keycloak.properties +++ b/quarkus/server/src/main/resources/META-INF/keycloak.properties @@ -1,9 +1,9 @@ hostname.default.frontendUrl = ${keycloak.frontendUrl:} -# Datasource -datasource.dialect=org.hibernate.dialect.H2Dialect +# Default Non-Production Grade Datasource +hibernate-orm.dialect=org.hibernate.dialect.H2Dialect datasource.driver=org.h2.jdbcx.JdbcDataSource -datasource.url = jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 +datasource.url = jdbc:h2:file:${keycloak.home.dir:~}/data/keycloakdb;;AUTO_SERVER=TRUE datasource.username = sa datasource.password = keycloak datasource.jdbc.transactions=xa \ No newline at end of file diff --git a/quarkus/server/src/main/resources/application.properties b/quarkus/server/src/main/resources/application.properties index 5d82aea3b63..090ff45bbd0 100644 --- a/quarkus/server/src/main/resources/application.properties +++ b/quarkus/server/src/main/resources/application.properties @@ -1,4 +1,9 @@ #quarkus.log.level = DEBUG +quarkus.package.output-name=keycloak +quarkus.package.type=mutable-jar +quarkus.package.output-directory=lib +quarkus.package.user-providers-directory=../providers + quarkus.http.root-path=/auth quarkus.application.name=Keycloak quarkus.banner.enabled=false \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/services/filters/AbstractClientConnectionFilter.java b/services/src/main/java/org/keycloak/services/filters/AbstractRequestFilter.java similarity index 76% rename from services/src/main/java/org/keycloak/services/filters/AbstractClientConnectionFilter.java rename to services/src/main/java/org/keycloak/services/filters/AbstractRequestFilter.java index 32f03e883a7..edf00ed353a 100644 --- a/services/src/main/java/org/keycloak/services/filters/AbstractClientConnectionFilter.java +++ b/services/src/main/java/org/keycloak/services/filters/AbstractRequestFilter.java @@ -18,8 +18,6 @@ package org.keycloak.services.filters; */ import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; import org.keycloak.common.ClientConnection; import org.keycloak.common.util.Resteasy; @@ -29,9 +27,9 @@ import org.keycloak.models.KeycloakTransactionManager; import org.keycloak.services.resources.KeycloakApplication; -public abstract class AbstractClientConnectionFilter { +public abstract class AbstractRequestFilter { - public void filter(ClientConnection clientConnection, Consumer next) { + protected void filter(ClientConnection clientConnection, Consumer next) { KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory(); KeycloakSession session = sessionFactory.create(); @@ -47,11 +45,13 @@ public abstract class AbstractClientConnectionFilter { tx.setRollbackOnly(); throw new RuntimeException(e); } finally { - close(session); + if (isAutoClose()) { + close(session); + } } } - public void close(KeycloakSession session) { + protected void close(KeycloakSession session) { KeycloakTransactionManager tx = session.getTransactionManager(); if (tx.isActive()) { if (tx.getRollbackOnly()) { @@ -63,4 +63,14 @@ public abstract class AbstractClientConnectionFilter { session.close(); } + + /** + *

Indicates whether or not resources should be close as part of the execution of the {@link #filter(ClientConnection, Consumer)} + * method. + * + * @return true if resources should be close automatically. Otherwise, false. + */ + protected boolean isAutoClose() { + return true; + } } \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/ant/configure.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/ant/configure.xml new file mode 100644 index 00000000000..6339db336b5 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/ant/configure.xml @@ -0,0 +1,21 @@ + + + + + Re-augmenting... + + + + + + + + + + + + + + + + diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml index f06783af573..54767c69d0f 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml @@ -172,6 +172,26 @@ + + org.apache.maven.plugins + maven-antrun-plugin + + + ant-generate-default + generate-resources + + run + + + + + ${auth.server.home} + + + + + + diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.properties b/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.properties index 8b753cef115..20ab953c3fe 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.properties +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.properties @@ -4,7 +4,7 @@ hostname.default.frontendUrl = ${keycloak.frontendUrl:} datasource.jdbc.transactions=xa # H2 -datasource.dialect=org.hibernate.dialect.H2Dialect +hibernate-orm.dialect=org.hibernate.dialect.H2Dialect datasource.driver=org.h2.jdbcx.JdbcDataSource datasource.url = jdbc:h2:file:${keycloak.home.dir}/data/keycloakdb;AUTO_SERVER=TRUE;DB_CLOSE_DELAY=-1 datasource.username = sa diff --git a/lsp/conf/properties.json b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/beans.xml similarity index 100% rename from lsp/conf/properties.json rename to testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/beans.xml diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java index ab251bdfc69..0ae2d7b313d 100644 --- a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java +++ b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java @@ -50,7 +50,7 @@ import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.testsuite.JsonConfigProviderFactory; import org.keycloak.testsuite.KeycloakServer; -import org.keycloak.testsuite.UndertowClientConnectionServletFilter; +import org.keycloak.testsuite.UndertowRequestFilter; import org.keycloak.testsuite.utils.tls.TLSUtils; import org.keycloak.testsuite.utils.undertow.UndertowDeployerHelper; import org.keycloak.testsuite.utils.undertow.UndertowWarClassLoader; @@ -99,7 +99,7 @@ public class KeycloakOnUndertow implements DeployableContainer keycloakConfigPropertyOverridesMap; private String profile; private String javaOpts; + private boolean reaugmentBeforeStart; @Override public void validate() throws ConfigurationException { @@ -136,4 +137,12 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration { public String getJavaOpts() { return javaOpts; } + + public boolean isReaugmentBeforeStart() { + return reaugmentBeforeStart; + } + + public void setReaugmentBeforeStart(boolean reaugmentBeforeStart) { + this.reaugmentBeforeStart = reaugmentBeforeStart; + } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakQuarkusServerDeployableContainer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakQuarkusServerDeployableContainer.java index a952035a4e3..30bb53e917f 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakQuarkusServerDeployableContainer.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakQuarkusServerDeployableContainer.java @@ -120,6 +120,19 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta FileUtils.deleteDirectory(configuration.getProvidersPath().resolve("data").toFile()); } + if (configuration.isReaugmentBeforeStart()) { + ProcessBuilder reaugment = new ProcessBuilder("./kc.sh", "config"); + + reaugment.directory(wrkDir).inheritIO(); + + try { + log.infof("Re-building the server with the new configuration"); + reaugment.start().waitFor(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException("Timeout while waiting for re-augmentation", e); + } + } + return builder.start(); } diff --git a/testsuite/performance/tests/src/test/java/org/keycloak/performance/dataset/DatasetTest.java b/testsuite/performance/tests/src/test/java/org/keycloak/performance/dataset/DatasetTest.java index a3568457d54..cfda09995ae 100644 --- a/testsuite/performance/tests/src/test/java/org/keycloak/performance/dataset/DatasetTest.java +++ b/testsuite/performance/tests/src/test/java/org/keycloak/performance/dataset/DatasetTest.java @@ -57,7 +57,7 @@ public class DatasetTest extends EntityTest implements Loggable { }; ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(Include.NON_NULL); - Map map = mapper.convertValue(realm, typeRef); + Map map = (Map) mapper.convertValue(realm, typeRef); map.put("index", 1000); logger().info("MAP:"); diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/KeycloakServer.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/KeycloakServer.java index 9550af92855..ddd3481e7bb 100755 --- a/testsuite/utils/src/main/java/org/keycloak/testsuite/KeycloakServer.java +++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/KeycloakServer.java @@ -22,9 +22,7 @@ import io.undertow.servlet.Servlets; import io.undertow.servlet.api.DefaultServletConfig; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.FilterInfo; -import io.undertow.servlet.api.ServletInfo; import org.jboss.logging.Logger; -import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher; import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters; import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer; import org.jboss.resteasy.spi.ResteasyDeployment; @@ -405,7 +403,7 @@ public class KeycloakServer { // KEYCLOAK-14178 deployment.setProperty(ResteasyContextParameters.RESTEASY_DISABLE_HTML_SANITIZER, true); - FilterInfo filter = Servlets.filter("SessionFilter", UndertowClientConnectionServletFilter.class); + FilterInfo filter = Servlets.filter("SessionFilter", UndertowRequestFilter.class); filter.setAsyncSupported(true); di.addFilter(filter); diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/UndertowClientConnectionServletFilter.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/UndertowRequestFilter.java similarity index 93% rename from testsuite/utils/src/main/java/org/keycloak/testsuite/UndertowClientConnectionServletFilter.java rename to testsuite/utils/src/main/java/org/keycloak/testsuite/UndertowRequestFilter.java index 3fa1d6c5feb..320762c6170 100755 --- a/testsuite/utils/src/main/java/org/keycloak/testsuite/UndertowClientConnectionServletFilter.java +++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/UndertowRequestFilter.java @@ -27,9 +27,9 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.keycloak.common.ClientConnection; -import org.keycloak.services.filters.AbstractClientConnectionFilter; +import org.keycloak.services.filters.AbstractRequestFilter; -public class UndertowClientConnectionServletFilter extends AbstractClientConnectionFilter implements Filter { +public class UndertowRequestFilter extends AbstractRequestFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) diff --git a/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/WildFlyClientConnectionServletFilter.java b/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/WildFlyRequestFilter.java similarity index 92% rename from wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/WildFlyClientConnectionServletFilter.java rename to wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/WildFlyRequestFilter.java index f682c73e4d4..5cbb00f84e6 100755 --- a/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/WildFlyClientConnectionServletFilter.java +++ b/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/WildFlyRequestFilter.java @@ -26,9 +26,9 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.keycloak.common.ClientConnection; -import org.keycloak.services.filters.AbstractClientConnectionFilter; +import org.keycloak.services.filters.AbstractRequestFilter; -public class WildFlyClientConnectionServletFilter extends AbstractClientConnectionFilter implements Filter { +public class WildFlyRequestFilter extends AbstractRequestFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)