Minor changes to pom files and fixing exception handling

This commit is contained in:
Pedro Igor 2020-10-25 21:55:25 -03:00 committed by Marek Posolda
parent c98048bf64
commit c929f81dd7
11 changed files with 76 additions and 120 deletions

View file

@ -56,6 +56,8 @@
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!-- Override the dependencies below to use the versions used by Quarkus -->
<dependency> <dependency>
<groupId>org.hibernate</groupId> <groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId> <artifactId>hibernate-core</artifactId>
@ -100,33 +102,6 @@
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-commons</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>1.0.8</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<modules> <modules>
<module>deployment</module> <module>deployment</module>
<module>runtime</module> <module>runtime</module>

View file

@ -441,6 +441,14 @@
<groupId>org.liquibase</groupId> <groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId> <artifactId>liquibase-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-commons</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>

View file

@ -14,6 +14,7 @@ import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
import org.jboss.resteasy.spi.ResteasyDeployment; import org.jboss.resteasy.spi.ResteasyDeployment;
import org.keycloak.common.util.Resteasy; import org.keycloak.common.util.Resteasy;
import org.keycloak.models.utils.PostMigrationEvent; import org.keycloak.models.utils.PostMigrationEvent;
import org.keycloak.provider.quarkus.QuarkusPlatform;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.resources.QuarkusWelcomeResource; import org.keycloak.services.resources.QuarkusWelcomeResource;
import org.keycloak.services.resources.WelcomeResource; import org.keycloak.services.resources.WelcomeResource;
@ -26,9 +27,13 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
@Override @Override
protected void startup() { protected void startup() {
try {
forceEntityManagerInitialization(); forceEntityManagerInitialization();
initializeKeycloakSessionFactory(); initializeKeycloakSessionFactory();
setupScheduledTasks(sessionFactory); setupScheduledTasks(sessionFactory);
} catch (Throwable cause) {
QuarkusPlatform.exitOnError(cause);
}
} }
@Override @Override

View file

@ -28,7 +28,7 @@ import org.keycloak.common.Profile;
import org.keycloak.configuration.PropertyMapper; import org.keycloak.configuration.PropertyMapper;
import org.keycloak.configuration.PropertyMappers; import org.keycloak.configuration.PropertyMappers;
import org.keycloak.platform.Platform; import org.keycloak.platform.Platform;
import org.keycloak.provider.quarkus.QuarkusConfigurationException; import org.keycloak.provider.quarkus.InitializationException;
import org.keycloak.provider.quarkus.QuarkusPlatform; import org.keycloak.provider.quarkus.QuarkusPlatform;
import org.keycloak.util.Environment; import org.keycloak.util.Environment;
import picocli.CommandLine; import picocli.CommandLine;
@ -136,16 +136,16 @@ final class Picocli {
if (throwable != null) { if (throwable != null) {
boolean verbose = cliArgs.stream().anyMatch((arg) -> "--verbose".equals(arg)); boolean verbose = cliArgs.stream().anyMatch((arg) -> "--verbose".equals(arg));
if (throwable instanceof QuarkusConfigurationException) { if (throwable instanceof InitializationException) {
QuarkusConfigurationException quarkusConfigException = (QuarkusConfigurationException) throwable; InitializationException initializationException = (InitializationException) throwable;
if (quarkusConfigException.getSuppressed() == null || quarkusConfigException.getSuppressed().length == 0) { if (initializationException.getSuppressed() == null || initializationException.getSuppressed().length == 0) {
dumpException(cmd, quarkusConfigException, verbose); dumpException(cmd, initializationException, verbose);
} else if (quarkusConfigException.getSuppressed().length == 1) { } else if (initializationException.getSuppressed().length == 1) {
dumpException(cmd, quarkusConfigException.getSuppressed()[0], verbose); dumpException(cmd, initializationException.getSuppressed()[0], verbose);
} else { } else {
logError(cmd, "ERROR: Multiple configuration errors during startup"); logError(cmd, "ERROR: Multiple configuration errors during startup");
int counter = 0; int counter = 0;
for (Throwable inner : quarkusConfigException.getSuppressed()) { for (Throwable inner : initializationException.getSuppressed()) {
counter++; counter++;
logError(cmd, "ERROR " + counter); logError(cmd, "ERROR " + counter);
dumpException(cmd, inner, verbose); dumpException(cmd, inner, verbose);

View file

@ -21,6 +21,7 @@ import static org.keycloak.configuration.PropertyMapper.MAPPERS;
import static org.keycloak.configuration.PropertyMapper.create; import static org.keycloak.configuration.PropertyMapper.create;
import static org.keycloak.configuration.PropertyMapper.createWithDefault; import static org.keycloak.configuration.PropertyMapper.createWithDefault;
import static org.keycloak.configuration.PropertyMapper.forBuildTimeProperty; import static org.keycloak.configuration.PropertyMapper.forBuildTimeProperty;
import static org.keycloak.provider.quarkus.QuarkusPlatform.addInitializationException;
import java.io.File; import java.io.File;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -67,7 +68,7 @@ public final class PropertyMappers {
} }
if (proceed == null || proceed.getValue() == null) { if (proceed == null || proceed.getValue() == null) {
addDeferredConfigurationException(Messages.httpsConfigurationNotSet()); addInitializationException(Messages.httpsConfigurationNotSet());
} }
} }
@ -114,7 +115,7 @@ public final class PropertyMappers {
case "passthrough": case "passthrough":
return "true"; return "true";
} }
addDeferredConfigurationException(Messages.invalidProxyMode(mode)); addInitializationException(Messages.invalidProxyMode(mode));
return "false"; return "false";
}, "The proxy mode if the server is behind a reverse proxy. Possible values are: none, edge, reencrypt, and passthrough."); }, "The proxy mode if the server is behind a reverse proxy. Possible values are: none, edge, reencrypt, and passthrough.");
} }
@ -167,7 +168,7 @@ public final class PropertyMappers {
case "postgres-10": case "postgres-10":
return "postgresql"; return "postgresql";
} }
addDeferredConfigurationException(invalidDatabaseVendor(db, "h2-file", "h2-mem", "mariadb", "mysql", "postgres", "postgres-95", "postgres-10")); addInitializationException(invalidDatabaseVendor(db, "h2-file", "h2-mem", "mariadb", "mysql", "postgres", "postgres-95", "postgres-10"));
return "h2"; return "h2";
}, "The database vendor. Possible values are: h2-mem, h2-file, mariadb, mysql, postgres95, postgres10."); }, "The database vendor. Possible values are: h2-mem, h2-file, mariadb, mysql, postgres95, postgres10.");
create("db", "quarkus.datasource.jdbc.transactions", (db, context) -> "xa", null); create("db", "quarkus.datasource.jdbc.transactions", (db, context) -> "xa", null);
@ -197,16 +198,7 @@ public final class PropertyMappers {
} }
private static void configureClustering() { private static void configureClustering() {
createWithDefault("cluster", "kc.spi.connections-infinispan.default.config-file", "placeholder", (value, context) -> { createWithDefault("cluster", "kc.spi.connections-infinispan.default.config-file", "default", (value, context) -> "cluster-" + value + ".xml", "Specifies clustering configuration. The specified value points to the infinispan configuration file prefixed with the 'cluster-` "
if ("placeholder".equals(value)) {
// Clustering is disabled by default for the "dev" profile. Otherwise enabled
value = ("dev".equalsIgnoreCase(ProfileManager.getActiveProfile())) ? "local" : "default";
}
return "cluster-" + value + ".xml";
}, "Specifies clustering configuration. The specified value points to the infinispan configuration file prefixed with the 'cluster-` "
+ "inside the distribution configuration directory. Supported values out of the box are 'local' and 'cluster'. Value 'local' points to the file cluster-local.xml and " + + "inside the distribution configuration directory. Supported values out of the box are 'local' and 'cluster'. Value 'local' points to the file cluster-local.xml and " +
"effectively disables clustering and use infinispan caches in the local mode. Value 'default' points to the file cluster-default.xml, which has clustering enabled for infinispan caches."); "effectively disables clustering and use infinispan caches in the local mode. Value 'default' points to the file cluster-default.xml, which has clustering enabled for infinispan caches.");
create("cluster-stack", "kc.spi.connections-infinispan.default.stack", "Specified the default stack to use for cluster communication and node discovery. Possible values are: tcp, udp, kubernetes, ec2."); create("cluster-stack", "kc.spi.connections-infinispan.default.stack", "Specified the default stack to use for cluster communication and node discovery. Possible values are: tcp, udp, kubernetes, ec2.");
@ -268,10 +260,4 @@ public final class PropertyMappers {
} }
}).findFirst().orElse(null); }).findFirst().orElse(null);
} }
// Exception to be thrown later, so that we can properly log it and collect all possible configuration issues (not just the first one)
private static void addDeferredConfigurationException(Exception e) {
QuarkusPlatform quarkusPlatform = (QuarkusPlatform) Platform.getPlatform();
quarkusPlatform.addDeferredException(e);
}
} }

View file

@ -24,5 +24,5 @@ package org.keycloak.provider.quarkus;
* *
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class QuarkusConfigurationException extends RuntimeException { public class InitializationException extends RuntimeException {
} }

View file

@ -40,16 +40,7 @@ public class QuarkusLifecycleObserver {
void onStartupEvent(@Observes StartupEvent event) { void onStartupEvent(@Observes StartupEvent event) {
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform(); QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
platform.started(); platform.started();
QuarkusPlatform.exitOnError();
// Check if we had any exceptions during configuration phase
if (!platform.getDeferredExceptions().isEmpty()) {
QuarkusConfigurationException quarkusException = new QuarkusConfigurationException();
for (Throwable inner : platform.getDeferredExceptions()) {
quarkusException.addSuppressed(inner);
}
throw quarkusException;
}
Runnable startupHook = platform.startupHook; Runnable startupHook = platform.startupHook;
if (startupHook != null) { if (startupHook != null) {

View file

@ -21,10 +21,48 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.keycloak.platform.Platform;
import org.keycloak.platform.PlatformProvider; import org.keycloak.platform.PlatformProvider;
public class QuarkusPlatform implements PlatformProvider { public class QuarkusPlatform implements PlatformProvider {
public static void addInitializationException(Throwable throwable) {
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
platform.addDeferredException(throwable);
}
/**
* <p>Throws a {@link InitializationException} exception to indicate errors during the startup.
*
* <p>Calling this method after the server is started has no effect but just the exception being thrown.
*
* @throws InitializationException the exception holding all errors during startup.
*/
public static void exitOnError() throws InitializationException {
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
// Check if we had any exceptions during initialization phase
if (!platform.getDeferredExceptions().isEmpty()) {
InitializationException quarkusException = new InitializationException();
for (Throwable inner : platform.getDeferredExceptions()) {
quarkusException.addSuppressed(inner);
}
throw quarkusException;
}
}
/**
* Similar behavior as per {@code #exitOnError} but convenient to throw a {@link InitializationException} with a single
* {@code cause}
*
* @param cause the cause
* @throws InitializationException the initialization exception with the given {@code cause}.
*/
public static void exitOnError(Throwable cause) throws InitializationException{
addInitializationException(cause);
exitOnError();
}
Runnable startupHook; Runnable startupHook;
Runnable shutdownHook; Runnable shutdownHook;
@ -49,7 +87,7 @@ public class QuarkusPlatform implements PlatformProvider {
/** /**
* Called when Quarkus platform is started * Called when Quarkus platform is started
*/ */
public void started() { void started() {
this.started.set(true); this.started.set(true);
} }
@ -62,11 +100,11 @@ public class QuarkusPlatform implements PlatformProvider {
* *
* @param t * @param t
*/ */
public void addDeferredException(Throwable t) { private void addDeferredException(Throwable t) {
deferredExceptions.add(t); deferredExceptions.add(t);
} }
public List<Throwable> getDeferredExceptions() { List<Throwable> getDeferredExceptions() {
return deferredExceptions; return deferredExceptions;
} }

View file

@ -234,8 +234,6 @@ public class ConfigurationTest {
public void testClusterConfig() { public void testClusterConfig() {
// Cluster enabled by default, but disabled for the "dev" profile // Cluster enabled by default, but disabled for the "dev" profile
Assert.assertEquals("cluster-default.xml", initConfig("connectionsInfinispan", "default").get("configFile")); Assert.assertEquals("cluster-default.xml", initConfig("connectionsInfinispan", "default").get("configFile"));
System.setProperty("kc.profile", "dev");
Assert.assertEquals("cluster-local.xml", initConfig("connectionsInfinispan", "default").get("configFile"));
// If explicitly set, then it is always used regardless of the profile // If explicitly set, then it is always used regardless of the profile
System.clearProperty("kc.profile"); System.clearProperty("kc.profile");

View file

@ -64,50 +64,4 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.version}</version>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<configuration>
<enableHttpUrlHandler>true</enableHttpUrlHandler>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemProperties>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project> </project>

View file

@ -5,3 +5,4 @@ db=h2-file
%dev.http.enabled=true %dev.http.enabled=true
%dev.db.username = sa %dev.db.username = sa
%dev.db.password = keycloak %dev.db.password = keycloak
%dev.cluster=local