org.jboss.spec.javax.ws.rs
jboss-jaxrs-api_2.1_spec
diff --git a/services/src/main/java/org/keycloak/common/util/Resteasy.java b/services/src/main/java/org/keycloak/common/util/Resteasy.java
new file mode 100644
index 0000000000..206247dc73
--- /dev/null
+++ b/services/src/main/java/org/keycloak/common/util/Resteasy.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2019 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.common.util;
+
+import org.jboss.resteasy.core.Dispatcher;
+import org.jboss.resteasy.core.ResteasyContext;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+/**
+ * Provides a layer of indirection to abstract invocations to Resteasy internal APIs. Making also possible to use different
+ * versions of Resteasy (e.g.: v3 and v4) depending on the stack that the server is running.
+ *
+ *
The methods herein provided are basically related with accessing context data from Resteasy, which changed in latest versions of Resteasy.
+ *
+ *
It is important to use this class when access to context data is necessary in order to avoid incompatibilities with future
+ * versions of Resteasy.
+ *
+ * @author Pedro Igor
+ */
+public final class Resteasy {
+
+ private static final BiConsumer PUSH_CONTEXT;
+ private static final BiConsumer PUSH_DEFAULT_OBJECT;
+ private static final Function PULL_CONTEXT;
+ private static final Runnable CLEAR_CONTEXT;
+
+ static {
+ if (isRestEasy4()) {
+ PUSH_CONTEXT = new BiConsumer() {
+ @Override
+ public void accept(Class p1, Object p2) {
+ ResteasyContext.pushContext(p1, p2);
+ }
+ };
+ PUSH_DEFAULT_OBJECT = new BiConsumer() {
+ @Override
+ public void accept(Class p1, Object p2) {
+ ResteasyContext.getContextData(org.jboss.resteasy.spi.Dispatcher.class).getDefaultContextObjects()
+ .put(p1, p2);
+ }
+ };
+ PULL_CONTEXT = new Function() {
+ @Override
+ public Object apply(Class p1) {
+ return ResteasyContext.getContextData(p1);
+ }
+ };
+ CLEAR_CONTEXT = new Runnable() {
+ @Override
+ public void run() {
+ ResteasyContext.clearContextData();
+ }
+ };
+ } else {
+ PUSH_CONTEXT = new BiConsumer() {
+ @Override
+ public void accept(Class p1, Object p2) {
+ ResteasyProviderFactory.getInstance().pushContext(p1, p2);
+ }
+ };
+ PUSH_DEFAULT_OBJECT = new BiConsumer() {
+ @Override
+ public void accept(Class p1, Object p2) {
+ ResteasyProviderFactory.getInstance().getContextData(Dispatcher.class).getDefaultContextObjects()
+ .put(p1, p2);
+ }
+ };
+ PULL_CONTEXT = new Function() {
+ @Override
+ public Object apply(Class p1) {
+ return ResteasyProviderFactory.getInstance().getContextData(p1);
+ }
+ };
+ CLEAR_CONTEXT = new Runnable() {
+ @Override
+ public void run() {
+ ResteasyProviderFactory.getInstance().clearContextData();
+ }
+ };
+ }
+ }
+
+ /**
+ * Push the given {@code instance} with type/key {@code type} to the Resteasy context associated with the current thread.
+ *
+ * @param type the type/key to associate the {@code instance} with
+ * @param instance the instance
+ */
+ public static void pushContext(Class type, Object instance) {
+ PUSH_CONTEXT.accept(type, instance);
+ }
+
+ /**
+ * Lookup the instance associated with the given type/key {@code type} from the Resteasy context associated with the current thread.
+ *
+ * @param type the type/key to lookup
+ * @return the instance associated with the given {@code type} or null if non-existent.
+ */
+ public static R getContextData(Class type) {
+ return (R) PULL_CONTEXT.apply(type);
+ }
+
+ /**
+ * Clear the Resteasy context associated with the current thread.
+ */
+ public static void clearContextData() {
+ CLEAR_CONTEXT.run();
+ }
+
+ /**
+ * Push the given {@code instance} with type/key {@code type} to the Resteasy global context.
+ *
+ * @param type the type/key to associate the {@code instance} with
+ * @param instance the instance
+ */
+ public static void pushDefaultContextObject(Class type, Object instance) {
+ PUSH_DEFAULT_OBJECT.accept(type, instance);
+ }
+
+ private static boolean isRestEasy4() {
+ try {
+ return Class.forName("org.jboss.resteasy.core.ResteasyContext") != null;
+ } catch (ClassNotFoundException ignore) {
+ return false;
+ }
+ }
+
+ /**
+ * Only necessary because keycloak-common is constrained to JDK 1.7.
+ */
+ private interface BiConsumer {
+ void accept(T p1, S p2);
+ }
+
+ /**
+ * Only necessary because keycloak-common is constrained to JDK 1.7.
+ */
+ private interface Function {
+ R apply(T p1);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
index 4600e63fcb..ec44a7db57 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
@@ -17,8 +17,8 @@
package org.keycloak.services;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.common.ClientConnection;
+import org.keycloak.common.util.Resteasy;
import org.keycloak.locale.LocaleSelectorProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakContext;
@@ -84,7 +84,7 @@ public class DefaultKeycloakContext implements KeycloakContext {
@Override
public T getContextObject(Class clazz) {
- return ResteasyProviderFactory.getContextData(clazz);
+ return Resteasy.getContextData(clazz);
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java b/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java
index 71d26b166d..72731bc7d8 100644
--- a/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java
+++ b/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java
@@ -5,6 +5,7 @@ import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
+import org.keycloak.common.util.Resteasy;
import org.keycloak.forms.login.freemarker.model.UrlBean;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
@@ -55,7 +56,7 @@ public class KeycloakErrorHandler implements ExceptionMapper {
@Override
public Response toResponse(Throwable throwable) {
- KeycloakTransaction tx = ResteasyProviderFactory.getContextData(KeycloakTransaction.class);
+ KeycloakTransaction tx = Resteasy.getContextData(KeycloakTransaction.class);
tx.setRollbackOnly();
int statusCode = getStatusCode(throwable);
diff --git a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
index 41876455c8..90f2b7008a 100755
--- a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
+++ b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
@@ -19,6 +19,7 @@ package org.keycloak.services.filters;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.common.ClientConnection;
+import org.keycloak.common.util.Resteasy;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakTransaction;
@@ -52,7 +53,7 @@ public class KeycloakSessionServletFilter implements Filter {
KeycloakSessionFactory sessionFactory = (KeycloakSessionFactory) servletRequest.getServletContext().getAttribute(KeycloakSessionFactory.class.getName());
KeycloakSession session = sessionFactory.create();
- ResteasyProviderFactory.pushContext(KeycloakSession.class, session);
+ Resteasy.pushContext(KeycloakSession.class, session);
ClientConnection connection = new ClientConnection() {
@Override
public String getRemoteAddr() {
@@ -80,10 +81,10 @@ public class KeycloakSessionServletFilter implements Filter {
}
};
session.getContext().setConnection(connection);
- ResteasyProviderFactory.pushContext(ClientConnection.class, connection);
+ Resteasy.pushContext(ClientConnection.class, connection);
KeycloakTransaction tx = session.getTransactionManager();
- ResteasyProviderFactory.pushContext(KeycloakTransaction.class, tx);
+ Resteasy.pushContext(KeycloakTransaction.class, tx);
tx.begin();
try {
@@ -128,7 +129,7 @@ public class KeycloakSessionServletFilter implements Filter {
}
session.close();
- ResteasyProviderFactory.clearContextData();
+ Resteasy.clearContextData();
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/filters/KeycloakTransactionCommitter.java b/services/src/main/java/org/keycloak/services/filters/KeycloakTransactionCommitter.java
index ecb321f556..a7e3415030 100644
--- a/services/src/main/java/org/keycloak/services/filters/KeycloakTransactionCommitter.java
+++ b/services/src/main/java/org/keycloak/services/filters/KeycloakTransactionCommitter.java
@@ -20,7 +20,7 @@
package org.keycloak.services.filters;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.common.util.Resteasy;
import org.keycloak.models.KeycloakTransaction;
import javax.ws.rs.container.ContainerRequestContext;
@@ -35,7 +35,7 @@ public class KeycloakTransactionCommitter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
- KeycloakTransaction tx = ResteasyProviderFactory.getContextData(KeycloakTransaction.class);
+ KeycloakTransaction tx = Resteasy.getContextData(KeycloakTransaction.class);
if (tx != null && tx.isActive()) {
if (tx.getRollbackOnly()) {
tx.rollback();
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 18178c0797..e7154e39e7 100644
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -22,8 +22,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import org.jboss.resteasy.core.Dispatcher;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
+import org.keycloak.common.util.Resteasy;
import org.keycloak.common.util.SystemEnvProperties;
import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.migration.MigrationModelManager;
@@ -106,19 +106,21 @@ public class KeycloakApplication extends Application {
protected KeycloakSessionFactory sessionFactory;
protected String contextPath;
- public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) {
+ public KeycloakApplication() {
try {
+ ServletContext context = Resteasy.getContextData(ServletContext.class);
+
if ("true".equals(context.getInitParameter(KEYCLOAK_EMBEDDED))) {
embedded = true;
}
-
+
loadConfig(context);
this.contextPath = context.getContextPath();
this.sessionFactory = createSessionFactory();
- dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this);
- ResteasyProviderFactory.pushContext(KeycloakApplication.class, this); // for injection
+ Resteasy.pushDefaultContextObject(KeycloakApplication.class, this);
+ Resteasy.pushContext(KeycloakApplication.class, this); // for injection
context.setAttribute(KeycloakSessionFactory.class.getName(), this.sessionFactory);
singletons.add(new RobotsResource());
diff --git a/services/src/main/java/org/keycloak/services/util/CacheControlUtil.java b/services/src/main/java/org/keycloak/services/util/CacheControlUtil.java
index b7b5c10bb9..b32fce76e1 100755
--- a/services/src/main/java/org/keycloak/services/util/CacheControlUtil.java
+++ b/services/src/main/java/org/keycloak/services/util/CacheControlUtil.java
@@ -18,8 +18,8 @@
package org.keycloak.services.util;
import org.jboss.resteasy.spi.HttpResponse;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
+import org.keycloak.common.util.Resteasy;
import javax.ws.rs.core.CacheControl;
@@ -29,7 +29,7 @@ import javax.ws.rs.core.CacheControl;
public class CacheControlUtil {
public static void noBackButtonCacheControlHeader() {
- HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
+ HttpResponse response = Resteasy.getContextData(HttpResponse.class);
response.getOutputHeaders().putSingle("Cache-Control", "no-store, must-revalidate, max-age=0");
}
diff --git a/services/src/main/java/org/keycloak/services/util/CookieHelper.java b/services/src/main/java/org/keycloak/services/util/CookieHelper.java
index b31f9f782b..b773d0b94f 100755
--- a/services/src/main/java/org/keycloak/services/util/CookieHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/CookieHelper.java
@@ -25,7 +25,7 @@ import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpResponse;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.common.util.Resteasy;
import org.keycloak.common.util.ServerCookie;
import javax.ws.rs.core.Cookie;
@@ -53,7 +53,7 @@ public class CookieHelper {
* @param httpOnly
*/
public static void addCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) {
- HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
+ HttpResponse response = Resteasy.getContextData(HttpResponse.class);
StringBuffer cookieBuf = new StringBuffer();
ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, comment, maxAge, secure, httpOnly);
String cookie = cookieBuf.toString();
@@ -62,7 +62,7 @@ public class CookieHelper {
public static Set getCookieValue(String name) {
- HttpHeaders headers = ResteasyProviderFactory.getContextData(HttpHeaders.class);
+ HttpHeaders headers = Resteasy.getContextData(HttpHeaders.class);
Set cookiesVal = new HashSet<>();
diff --git a/services/src/main/java/org/keycloak/services/util/P3PHelper.java b/services/src/main/java/org/keycloak/services/util/P3PHelper.java
index 4777d13277..6539c4dc17 100644
--- a/services/src/main/java/org/keycloak/services/util/P3PHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/P3PHelper.java
@@ -18,7 +18,7 @@
package org.keycloak.services.util;
import org.jboss.resteasy.spi.HttpResponse;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.common.util.Resteasy;
/**
* IE requires P3P header to allow loading cookies from iframes when domain differs from main page (see KEYCLOAK-2828 for more details)
@@ -28,7 +28,7 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
public class P3PHelper {
public static void addP3PHeader() {
- HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
+ HttpResponse response = Resteasy.getContextData(HttpResponse.class);
response.getOutputHeaders().putSingle("P3P", "CP=\"This is not a P3P policy!\"");
}
diff --git a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProvider.java b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProvider.java
index d5a1a06f64..17b1a0bceb 100755
--- a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProvider.java
+++ b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProvider.java
@@ -16,7 +16,6 @@
*/
package org.keycloak.social.google;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.OAuth2Constants;
import org.keycloak.broker.oidc.OIDCIdentityProvider;
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
@@ -63,7 +62,7 @@ public class GoogleIdentityProvider extends OIDCIdentityProvider implements Soci
protected String getUserInfoUrl() {
String uri = super.getUserInfoUrl();
if (((GoogleIdentityProviderConfig)getConfig()).isUserIp()) {
- ClientConnection connection = ResteasyProviderFactory.getContextData(ClientConnection.class);
+ ClientConnection connection = session.getContext().getConnection();
if (connection != null) {
uri = KeycloakUriBuilder.fromUri(super.getUserInfoUrl()).queryParam("userIp", connection.getRemoteAddr()).build().toString();
}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
index b854fffe89..fe51ff3e72 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
@@ -133,7 +133,7 @@ public class TestApplicationResourceProvider implements RealmResourceProvider {
sb.append("" + title + "");
sb.append("Form parameters:
");
- HttpRequest request = ResteasyProviderFactory.getContextData(HttpRequest.class);
+ HttpRequest request = session.getContext().getContextObject(HttpRequest.class);
MultivaluedMap formParams = request.getDecodedFormParameters();
for (String paramName : formParams.keySet()) {
sb.append(paramName).append(": ").append("" + title + "");
sb.append("Form parameters:
");
- HttpRequest request = ResteasyProviderFactory.getContextData(HttpRequest.class);
+ HttpRequest request = session.getContext().getContextObject(HttpRequest.class);
MultivaluedMap formParams = request.getDecodedFormParameters();
for (String paramName : formParams.keySet()) {
sb.append(paramName).append(": ").append("").append(formParams.getFirst(paramName)).append("
");