mirror of
https://github.com/keycloak/keycloak.git
synced 2026-05-26 13:50:48 +00:00
updates for quarkus:dev mode (#47848)
* fix: ensuring dev mode can fully reload the application closes: #47077 Signed-off-by: Steve Hawkins <shawkins@redhat.com> * also removing test launch mode Signed-off-by: Steve Hawkins <shawkins@redhat.com> * refining embedded mode cleanup Signed-off-by: Steve Hawkins <shawkins@redhat.com> # Conflicts: # quarkus/tests/junit5/src/main/java/org/keycloak/Keycloak.java * restoring test launch mode Signed-off-by: Steve Hawkins <shawkins@redhat.com> --------- Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
@@ -66,6 +66,10 @@ public class Environment {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if dev mode - note that non server commands may switch to non-server mode
|
||||
* at runtime. See {@link #isNonServerMode()}
|
||||
*/
|
||||
public static boolean isDevMode() {
|
||||
return DEV_PROFILE_VALUE.equalsIgnoreCase(getProfile());
|
||||
}
|
||||
@@ -80,6 +84,10 @@ public class Environment {
|
||||
return System.getenv(ENV_PROFILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if running in non-server mode - valid only as a runtime check.
|
||||
* <br>At build time, we build as prod or dev.
|
||||
*/
|
||||
public static boolean isNonServerMode() {
|
||||
return NON_SERVER_MODE.equalsIgnoreCase(Environment.getProfile());
|
||||
}
|
||||
|
||||
+2
-4
@@ -165,14 +165,12 @@ For debugging the build steps right after start, you can suspend the JVM by runn
|
||||
|
||||
**Expected behavior:** The server will start on **http://localhost:8080**.
|
||||
|
||||
When running using `quarkus:dev` you are able to do live coding whenever you change / add code in the `server` module, for example when creating a new custom provider.
|
||||
|
||||
There are currently limitations when running in development mode that block us to use all capabilities the Quarkus development mode has to offer.
|
||||
The main limitations you'll find at the moment are:
|
||||
|
||||
* Changes are only automatically reflected at runtime if you are changing resources from the `deployment`, `runtime`, and `server` modules. Other modules, such as `keycloak-services` still rely on Hot Swap in Java debuggers to reload classes.
|
||||
* Changes are only automatically reflected at runtime if you are changing resources from the quarkus `deployment`, `runtime`, and `server` modules. Non-quarkus modules, such as `keycloak-services`, still rely on Hot Swap in Java debuggers to reload classes.
|
||||
* There is nothing in the Dev UI related to the server itself, although you can still change some configuration from there.
|
||||
* There are some limitations when passing some options when running in dev mode. You should expect more improvements in this area.
|
||||
* quarkus.args does not honor build time options. Those instead must also be set via -D arguments, or environment variables, and thus require rerunning the quarkus:dev command to change.
|
||||
|
||||
### Debugging the server distribution
|
||||
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package org.keycloak.quarkus.deployment;
|
||||
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import io.quarkus.deployment.IsTest;
|
||||
import io.quarkus.runtime.LaunchMode;
|
||||
|
||||
public class IsIntegrationTest extends IsTest {
|
||||
|
||||
public IsIntegrationTest(LaunchMode launchMode) {
|
||||
super(launchMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAsBoolean() {
|
||||
return super.getAsBoolean() && Environment.isTestLaunchMode();
|
||||
}
|
||||
|
||||
}
|
||||
+1
-32
@@ -91,7 +91,6 @@ import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||
import org.keycloak.quarkus.runtime.configuration.PropertyMappingInterceptor;
|
||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||
import org.keycloak.quarkus.runtime.configuration.mappers.WildcardPropertyMapper;
|
||||
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
||||
@@ -130,7 +129,6 @@ import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
|
||||
import io.quarkus.arc.deployment.BuildTimeConditionBuildItem;
|
||||
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
|
||||
import io.quarkus.bootstrap.logging.InitialConfigurator;
|
||||
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceResultBuildItem;
|
||||
import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig;
|
||||
import io.quarkus.deployment.IsDevelopment;
|
||||
import io.quarkus.deployment.annotations.BuildProducer;
|
||||
@@ -749,40 +747,11 @@ class KeycloakProcessor {
|
||||
*
|
||||
* @param configSources
|
||||
*/
|
||||
@BuildStep(onlyIfNot = IsIntegrationTest.class )
|
||||
@BuildStep
|
||||
void configureConfigSources(BuildProducer<StaticInitConfigBuilderBuildItem> configSources) {
|
||||
configSources.produce(new StaticInitConfigBuilderBuildItem(KeycloakConfigSourceProvider.class.getName()));
|
||||
}
|
||||
|
||||
@BuildStep(onlyIf = IsIntegrationTest.class)
|
||||
void prepareTestEnvironment(BuildProducer< StaticInitConfigBuilderBuildItem> configSources, DevServicesDatasourceResultBuildItem dbConfig) {
|
||||
configSources.produce(new StaticInitConfigBuilderBuildItem("org.keycloak.quarkus.runtime.configuration.test.TestKeycloakConfigSourceProvider"));
|
||||
|
||||
// we do not enable dev services by default and the DevServicesDatasourceResultBuildItem might not be available when discovering build steps
|
||||
// Quarkus seems to allow that when the DevServicesDatasourceResultBuildItem is not the only parameter to the build step
|
||||
// this might be too sensitive and break if Quarkus changes the behavior
|
||||
if (dbConfig != null && dbConfig.getDefaultDatasource() != null) {
|
||||
Map<String, String> configProperties = dbConfig.getDefaultDatasource().getConfigProperties();
|
||||
|
||||
for (Entry<String, String> dbConfigProperty : configProperties.entrySet()) {
|
||||
PropertyMapper<?> mapper = PropertyMappers.getMapper(dbConfigProperty.getKey());
|
||||
|
||||
if (mapper == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String kcProperty = mapper.getFrom();
|
||||
|
||||
if (kcProperty.endsWith("db")) {
|
||||
// db kind set when running tests
|
||||
continue;
|
||||
}
|
||||
|
||||
System.setProperty(kcProperty, dbConfigProperty.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Make the build time configuration available at runtime so that the server can run without having to specify some of
|
||||
* the properties again.
|
||||
|
||||
@@ -91,6 +91,9 @@ public final class Environment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the we're currently in or built as dev mode.
|
||||
*/
|
||||
public static boolean isDevMode() {
|
||||
if (org.keycloak.common.util.Environment.isDevMode()) {
|
||||
return true;
|
||||
@@ -100,7 +103,7 @@ public final class Environment {
|
||||
}
|
||||
|
||||
public static boolean isDevProfile(){
|
||||
return Optional.ofNullable(org.keycloak.common.util.Environment.getProfile()).orElse("").equalsIgnoreCase(org.keycloak.common.util.Environment.DEV_PROFILE_VALUE);
|
||||
return org.keycloak.common.util.Environment.isDevMode();
|
||||
}
|
||||
|
||||
public static boolean isWindows() {
|
||||
|
||||
@@ -66,11 +66,11 @@ public class KeycloakMain implements QuarkusApplication {
|
||||
ensureForkJoinPoolThreadFactoryHasBeenSetToQuarkus();
|
||||
InfinispanUtils.ensureVirtualThreadsParallelism();
|
||||
|
||||
System.setProperty("kc.version", Version.VERSION);
|
||||
|
||||
Picocli picocli;
|
||||
Properties clonedProps = null;
|
||||
if (!(Thread.currentThread().getContextClassLoader() instanceof RunnerClassLoader)) {
|
||||
picocli = new Picocli() { // embedded launch case, avoid System.exit
|
||||
clonedProps = (Properties) System.getProperties().clone();
|
||||
picocli = new Picocli() { // non-script launch case, avoid System.exit
|
||||
@Override
|
||||
public void exit(int exitCode) {
|
||||
Quarkus.asyncExit(exitCode);
|
||||
@@ -79,7 +79,16 @@ public class KeycloakMain implements QuarkusApplication {
|
||||
} else {
|
||||
picocli = new Picocli();
|
||||
}
|
||||
main(args, picocli);
|
||||
|
||||
System.setProperty("kc.version", Version.VERSION);
|
||||
|
||||
try {
|
||||
main(args, picocli);
|
||||
} finally {
|
||||
if (clonedProps != null) {
|
||||
reset(clonedProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void reset(Properties systemProperties) {
|
||||
|
||||
@@ -157,6 +157,8 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev");
|
||||
assertFalse(nonRunningPicocli.getOutString(), nonRunningPicocli.getOutString().toUpperCase().contains("WARN"));
|
||||
assertFalse(nonRunningPicocli.getOutString(), nonRunningPicocli.getOutString().toUpperCase().contains("ERROR"));
|
||||
onAfter();
|
||||
assertFalse(Environment.isDevProfile());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -178,6 +179,7 @@ public class Keycloak {
|
||||
private List<Dependency> dependencies;
|
||||
private boolean fipsEnabled;
|
||||
private Properties systemProperties;
|
||||
private CountDownLatch closed;
|
||||
|
||||
public Keycloak() {
|
||||
this(null, Version.VERSION, List.of(), false);
|
||||
@@ -212,6 +214,8 @@ public class Keycloak {
|
||||
return this;
|
||||
}
|
||||
StartupAction startupAction = action.createInitialRuntimeApplication();
|
||||
closed = new CountDownLatch(1);
|
||||
startupAction.addRuntimeCloseTask(closed::countDown);
|
||||
application = startupAction.runMainClass(args.toArray(new String[0]));
|
||||
|
||||
return this;
|
||||
@@ -304,6 +308,11 @@ public class Keycloak {
|
||||
} catch (Exception cause) {
|
||||
cause.printStackTrace();
|
||||
}
|
||||
try {
|
||||
closed.await(); // wait for an orderly completion of all cleanup in the other thread
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
QuarkusConfigFactory.setConfig(null);
|
||||
|
||||
-16
@@ -22,7 +22,6 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -34,7 +33,6 @@ import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.cli.command.DryRunMixin;
|
||||
import org.keycloak.quarkus.runtime.cli.command.Start;
|
||||
import org.keycloak.quarkus.runtime.cli.command.StartDev;
|
||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||
|
||||
import io.quarkus.deployment.util.FileUtil;
|
||||
import io.quarkus.runtime.configuration.QuarkusConfigFactory;
|
||||
@@ -42,8 +40,6 @@ import io.quarkus.test.junit.QuarkusMainTestExtension;
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext.Store;
|
||||
import org.junit.jupiter.api.extension.ParameterContext;
|
||||
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
|
||||
@@ -56,7 +52,6 @@ import static org.keycloak.quarkus.runtime.Environment.forceTestLaunchMode;
|
||||
|
||||
public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
|
||||
private static final String SYS_PROPS = "sys-props";
|
||||
private KeycloakDistribution dist;
|
||||
private DatabaseContainer databaseContainer;
|
||||
private InfinispanContainer infinispanContainer;
|
||||
@@ -66,7 +61,6 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
DistributionTest distConfig = getDistributionConfig(context);
|
||||
Launch launch = context.getRequiredTestMethod().getAnnotation(Launch.class);
|
||||
getStore(context).put(SYS_PROPS, new HashMap<>(System.getProperties()));
|
||||
|
||||
if (launch != null && distConfig == null) {
|
||||
Stream.of(launch.value()).forEach(arg -> {
|
||||
@@ -117,10 +111,6 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
}
|
||||
}
|
||||
|
||||
private Store getStore(ExtensionContext context) {
|
||||
return context.getStore(Namespace.create(context.getRequiredTestClass(), context.getRequiredTestMethod()));
|
||||
}
|
||||
|
||||
private static Storage getStoreConfig(ExtensionContext context) {
|
||||
return context.getTestClass().get().getDeclaredAnnotation(Storage.class);
|
||||
}
|
||||
@@ -193,12 +183,6 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
|
||||
private void reset(DistributionTest distConfig, ExtensionContext context) {
|
||||
QuarkusConfigFactory.setConfig(null);
|
||||
HashMap props = getStore(context).remove(SYS_PROPS, HashMap.class);
|
||||
System.getProperties().clear();
|
||||
System.getProperties().putAll(props);
|
||||
// TODO: for in-vm tests this is not all that it takes to reset static state
|
||||
// may want to call AbstractConfigurationTest.resetConfiguration
|
||||
Configuration.resetConfig();
|
||||
if (databaseContainer != null && databaseContainer.isRunning()) {
|
||||
databaseContainer.stop();
|
||||
databaseContainer = null;
|
||||
|
||||
Reference in New Issue
Block a user