Skip to content

Commit baef63a

Browse files
committed
wip...
1 parent 8e068d8 commit baef63a

File tree

8 files changed

+118
-91
lines changed

8 files changed

+118
-91
lines changed

quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.keycloak.config.BootstrapAdminOptions;
2424
import org.keycloak.connections.jpa.JpaConnectionProvider;
2525
import org.keycloak.models.KeycloakSession;
26-
import org.keycloak.models.KeycloakSessionFactory;
2726
import org.keycloak.quarkus.runtime.Environment;
2827
import org.keycloak.quarkus.runtime.configuration.Configuration;
2928
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
@@ -43,13 +42,12 @@
4342

4443
@ApplicationPath("/")
4544
@Blocking
46-
public class QuarkusKeycloakApplication extends KeycloakApplication {
45+
public class QuarkusKeycloakApplication extends KeycloakApplication<QuarkusKeycloakSessionFactory> {
4746

4847
private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN";
4948
private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD";
5049

5150
private static final Logger logger = Logger.getLogger(QuarkusKeycloakApplication.class);
52-
private static boolean bootstrapCompleted = false;
5351

5452
@Override
5553
protected String getDataDir() {
@@ -73,10 +71,13 @@ void onShutdownEvent(@Observes ShutdownEvent event) {
7371
}
7472

7573
@Override
76-
public KeycloakSessionFactory createSessionFactory() {
77-
QuarkusKeycloakSessionFactory instance = QuarkusKeycloakSessionFactory.getInstance();
78-
instance.init();
79-
return instance;
74+
public QuarkusKeycloakSessionFactory createSessionFactory() {
75+
return QuarkusKeycloakSessionFactory.getInstance();
76+
}
77+
78+
@Override
79+
protected void initKeycloakSessionFactory(QuarkusKeycloakSessionFactory quarkusKeycloakSessionFactory) {
80+
quarkusKeycloakSessionFactory.init();
8081
}
8182

8283
@Override
@@ -105,23 +106,14 @@ protected void createTemporaryAdmin(KeycloakSession session) {
105106
}
106107
}
107108

108-
@Override
109-
protected void onInitializationCompleted() {
110-
bootstrapCompleted = true;
111-
}
112-
113109
@Override
114110
protected boolean supportsAsyncInitialization() {
115111
// TODO support only for production environment, dev and non server probably won't need to do async init
116112
return !org.keycloak.common.util.Environment.isNonServerMode();
117113
}
118114

119-
public static boolean isBootstrapCompleted() {
120-
return bootstrapCompleted;
121-
}
122-
123115
@Override
124-
protected int getTransactionTimeout(KeycloakSessionFactory sessionFactory) {
116+
protected int getTransactionTimeout(QuarkusKeycloakSessionFactory sessionFactory) {
125117
return ((QuarkusJpaConnectionProviderFactory) sessionFactory.getProviderFactory(JpaConnectionProvider.class)).getMigrationTransactionTimeout();
126118
}
127119

quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/BootstrapFilter.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,28 @@
44
import jakarta.ws.rs.container.ContainerRequestContext;
55
import jakarta.ws.rs.core.Response;
66

7-
import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication;
7+
import org.keycloak.services.resources.KeycloakApplication;
88

99
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
1010

1111
@ApplicationScoped
1212
public class BootstrapFilter {
1313

14-
@ServerRequestFilter(priority = 1)
15-
public Response filter(ContainerRequestContext requestContext) {
16-
if (QuarkusKeycloakApplication.isBootstrapCompleted()) {
14+
private boolean ready;
15+
16+
@ServerRequestFilter(priority = 1, preMatching = true)
17+
public Response filter(ContainerRequestContext ignored) {
18+
if (ready) {
19+
// JVM branch prediction may optimize this code and saves on reading a static volatile field
20+
return null;
21+
}
22+
if (KeycloakApplication.isBootstrapCompleted()) {
1723
// Return null to continue the request chain normally
24+
ready = true;
1825
return null;
1926
}
2027
// Return 503 Service Unavailable
21-
return Response.status(Response.Status.SERVICE_UNAVAILABLE)
22-
.entity("Keycloak is initializing...")
23-
.build();
28+
return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
2429

2530
}
2631
}

quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/BoostrapReadyHealthCheck.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,42 @@
1919

2020
import jakarta.enterprise.context.ApplicationScoped;
2121

22-
import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication;
22+
import org.keycloak.services.resources.KeycloakApplication;
2323

2424
import io.smallrye.health.api.AsyncHealthCheck;
2525
import io.smallrye.mutiny.Uni;
2626
import org.eclipse.microprofile.health.HealthCheckResponse;
27+
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
2728
import org.eclipse.microprofile.health.Readiness;
2829

2930
@Readiness
3031
@ApplicationScoped
3132
public class BoostrapReadyHealthCheck implements AsyncHealthCheck {
3233

34+
private static final HealthCheckResponse UP = builder().up().build();
35+
private boolean bootstrapCompleted;
36+
3337
@Override
3438
public Uni<HealthCheckResponse> call() {
35-
var builder = HealthCheckResponse.named("Keycloak Initialized");
36-
if (QuarkusKeycloakApplication.isBootstrapCompleted()) {
39+
// JVM branch prediction may optimize this code and saves on reading a static volatile field
40+
if (bootstrapCompleted) {
41+
return ready();
42+
}
43+
var builder = builder();
44+
if (KeycloakApplication.isBootstrapCompleted()) {
3745
builder.up();
46+
bootstrapCompleted = true;
3847
} else {
3948
builder.down();
4049
}
4150
return Uni.createFrom().item(builder.build());
4251
}
52+
53+
private Uni<HealthCheckResponse> ready() {
54+
return Uni.createFrom().item(UP);
55+
}
56+
57+
private static HealthCheckResponseBuilder builder() {
58+
return HealthCheckResponse.named("Keycloak Initialized");
59+
}
4360
}

quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakClusterReadyHealthCheck.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@
1919
import java.time.Instant;
2020
import java.util.concurrent.atomic.AtomicReference;
2121

22-
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
2322
import org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory;
24-
import org.keycloak.infinispan.util.InfinispanUtils;
25-
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
2623

2724
import io.smallrye.health.api.AsyncHealthCheck;
2825
import io.smallrye.mutiny.Uni;
@@ -33,16 +30,16 @@
3330

3431
public class KeycloakClusterReadyHealthCheck implements AsyncHealthCheck {
3532

36-
private static final AtomicReference<Instant> failingSince = new AtomicReference<>();
33+
private final AtomicReference<Instant> failingSince = new AtomicReference<>();
34+
private final InfinispanConnectionProviderFactory factory;
35+
36+
public KeycloakClusterReadyHealthCheck(InfinispanConnectionProviderFactory factory) {
37+
this.factory = factory;
38+
}
3739

3840
@Override
3941
public Uni<HealthCheckResponse> call() {
4042
var builder = HealthCheckResponse.named("Keycloak cluster health check").up();
41-
if (InfinispanUtils.isRemoteInfinispan()) {
42-
return Uni.createFrom().item(builder.build());
43-
}
44-
var sessionFactory = QuarkusKeycloakSessionFactory.getInstance();
45-
InfinispanConnectionProviderFactory factory = (InfinispanConnectionProviderFactory) sessionFactory.getProviderFactory(InfinispanConnectionProvider.class);
4643
if (factory.isClusterHealthy()) {
4744
failingSince.set(null);
4845
} else {

quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakClusterReadyHealthCheckProducer.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,47 @@
1616
*/
1717
package org.keycloak.quarkus.runtime.services.health;
1818

19+
1920
import jakarta.enterprise.context.ApplicationScoped;
2021
import jakarta.enterprise.context.Dependent;
2122
import jakarta.enterprise.inject.Produces;
2223

2324
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
2425
import org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory;
25-
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
26-
import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication;
26+
import org.keycloak.services.resources.KeycloakApplication;
2727

2828
import io.smallrye.health.api.AsyncHealthCheck;
2929
import org.eclipse.microprofile.health.Readiness;
3030

3131
@ApplicationScoped
3232
public class KeycloakClusterReadyHealthCheckProducer {
3333

34+
private AsyncHealthCheck instance;
35+
private boolean ready;
36+
3437
@Produces
3538
@Readiness
3639
@Dependent
3740
public AsyncHealthCheck createHealthCheck() {
38-
if (!QuarkusKeycloakApplication.isBootstrapCompleted()) {
39-
return null;
41+
if (ready) {
42+
// JVM branch prediction may optimize this code and saves on reading a static volatile field
43+
return instance;
4044
}
41-
var sessionFactory = QuarkusKeycloakSessionFactory.getInstance();
42-
InfinispanConnectionProviderFactory factory = (InfinispanConnectionProviderFactory) sessionFactory.getProviderFactory(InfinispanConnectionProvider.class);
43-
if (factory.isClusterHealthSupported()) {
44-
return new KeycloakClusterReadyHealthCheck();
45-
} else {
45+
if (!KeycloakApplication.isBootstrapCompleted()) {
4646
return null;
4747
}
48+
synchronized (this) {
49+
if (ready) {
50+
return instance;
51+
}
52+
var sessionFactory = KeycloakApplication.getSessionFactory();
53+
var factory = (InfinispanConnectionProviderFactory) sessionFactory.getProviderFactory(InfinispanConnectionProvider.class);
54+
if (factory.isClusterHealthSupported()) {
55+
instance = new KeycloakClusterReadyHealthCheck(factory);
56+
}
57+
ready = true;
58+
}
59+
60+
return instance;
4861
}
4962
}

services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.keycloak.forms.login.MessageType;
2020
import org.keycloak.forms.login.freemarker.model.UrlBean;
2121
import org.keycloak.models.KeycloakSession;
22-
import org.keycloak.models.KeycloakSessionTaskWithResult;
2322
import org.keycloak.models.KeycloakTransaction;
2423
import org.keycloak.models.ModelDuplicateException;
2524
import org.keycloak.models.ModelException;
@@ -30,6 +29,7 @@
3029
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
3130
import org.keycloak.services.managers.RealmManager;
3231
import org.keycloak.services.messages.Messages;
32+
import org.keycloak.services.resources.KeycloakApplication;
3333
import org.keycloak.theme.Theme;
3434
import org.keycloak.theme.beans.AdvancedMessageFormatterMethod;
3535
import org.keycloak.theme.beans.LocaleBean;
@@ -58,17 +58,16 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
5858

5959
@Override
6060
public Response toResponse(Throwable throwable) {
61+
if (!KeycloakApplication.isBootstrapCompleted()) {
62+
logger.warn("Exception suppressed because Keycloak is not initialized", throwable);
63+
return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
64+
}
6165
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
6266

6367
if (session == null) {
6468
// errors might be thrown when handling errors from JAX-RS before the session is available
6569
return KeycloakModelUtils.runJobInTransactionWithResult(getSessionFactory(),
66-
new KeycloakSessionTaskWithResult<Response>() {
67-
@Override
68-
public Response run(KeycloakSession session) {
69-
return getResponse(session, throwable);
70-
}
71-
});
70+
session1 -> getResponse(session1, throwable));
7271
}
7372

7473
return getResponse(session, throwable);
@@ -219,7 +218,7 @@ private static Map<String, Object> initAttributes(KeycloakSession session, Realm
219218
attributes.put("darkMode", "true".equals(properties.getProperty("darkMode"))
220219
&& realm.getAttribute("darkMode", true));
221220
} catch (IOException e) {
222-
e.printStackTrace();
221+
logger.error(e);
223222
}
224223

225224
return attributes;

0 commit comments

Comments
 (0)