Update WebLogicRequestUpgradeStrategy for WLS 12.2.1
Issue: SPR-13234
This commit is contained in:
parent
1d60a6a6af
commit
3e807c297d
|
|
@ -21,7 +21,7 @@ import java.lang.reflect.Constructor;
|
|||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import javax.servlet.ServletException;
|
||||
|
|
@ -53,6 +53,8 @@ import org.springframework.util.StringUtils;
|
|||
import org.springframework.web.socket.WebSocketExtension;
|
||||
import org.springframework.web.socket.server.HandshakeFailureException;
|
||||
|
||||
import static org.glassfish.tyrus.spi.WebSocketEngine.UpgradeStatus.SUCCESS;
|
||||
|
||||
/**
|
||||
* A base class for {@code RequestUpgradeStrategy} implementations on top of
|
||||
* JSR-356 based servers which include Tyrus as their WebSocket engine.
|
||||
|
|
@ -96,6 +98,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
TyrusServerContainer serverContainer = (TyrusServerContainer) getContainer(servletRequest);
|
||||
TyrusWebSocketEngine engine = (TyrusWebSocketEngine) serverContainer.getWebSocketEngine();
|
||||
Object tyrusEndpoint = null;
|
||||
boolean success;
|
||||
|
||||
try {
|
||||
// Shouldn't matter for processing but must be unique
|
||||
|
|
@ -107,29 +110,22 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
RequestContext requestContext = createRequestContext(servletRequest, path, headers);
|
||||
TyrusUpgradeResponse upgradeResponse = new TyrusUpgradeResponse();
|
||||
UpgradeInfo upgradeInfo = engine.upgrade(requestContext, upgradeResponse);
|
||||
|
||||
switch (upgradeInfo.getStatus()) {
|
||||
case SUCCESS:
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Successful upgrade: " + upgradeResponse.getHeaders());
|
||||
}
|
||||
handleSuccess(servletRequest, servletResponse, upgradeInfo, upgradeResponse);
|
||||
break;
|
||||
case HANDSHAKE_FAILED:
|
||||
// Should never happen
|
||||
throw new HandshakeFailureException("Unexpected handshake failure: " + request.getURI());
|
||||
case NOT_APPLICABLE:
|
||||
// Should never happen
|
||||
throw new HandshakeFailureException("Unexpected handshake mapping failure: " + request.getURI());
|
||||
success = SUCCESS.equals(upgradeInfo.getStatus());
|
||||
if (success) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Successful upgrade: " + upgradeResponse.getHeaders());
|
||||
}
|
||||
handleSuccess(servletRequest, servletResponse, upgradeInfo, upgradeResponse);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
unregisterTyrusEndpoint(engine, tyrusEndpoint);
|
||||
throw new HandshakeFailureException("Error during handshake: " + request.getURI(), ex);
|
||||
}
|
||||
finally {
|
||||
if (tyrusEndpoint != null) {
|
||||
getEndpointHelper().unregister(engine, tyrusEndpoint);
|
||||
}
|
||||
|
||||
unregisterTyrusEndpoint(engine, tyrusEndpoint);
|
||||
if (!success) {
|
||||
throw new HandshakeFailureException("Unexpected handshake failure: " + request.getURI());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +134,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
throws DeploymentException {
|
||||
|
||||
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(endpointPath, endpoint);
|
||||
endpointConfig.setSubprotocols(Arrays.asList(protocol));
|
||||
endpointConfig.setSubprotocols(Collections.singletonList(protocol));
|
||||
endpointConfig.setExtensions(extensions);
|
||||
return getEndpointHelper().createdEndpoint(endpointConfig, this.componentProvider, container, engine);
|
||||
}
|
||||
|
|
@ -157,6 +153,16 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
return context;
|
||||
}
|
||||
|
||||
private void unregisterTyrusEndpoint(TyrusWebSocketEngine engine, Object tyrusEndpoint) {
|
||||
if (tyrusEndpoint != null) {
|
||||
try {
|
||||
getEndpointHelper().unregister(engine, tyrusEndpoint);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract TyrusEndpointHelper getEndpointHelper();
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.websocket.CloseReason;
|
||||
|
||||
import org.glassfish.tyrus.core.TyrusEndpointWrapper;
|
||||
import org.glassfish.tyrus.core.TyrusUpgradeResponse;
|
||||
import org.glassfish.tyrus.core.TyrusWebSocketEngine;
|
||||
import org.glassfish.tyrus.core.Utils;
|
||||
import org.glassfish.tyrus.spi.Connection;
|
||||
import org.glassfish.tyrus.spi.WebSocketEngine.UpgradeInfo;
|
||||
|
|
@ -38,24 +40,47 @@ import org.glassfish.tyrus.spi.Writer;
|
|||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.socket.server.HandshakeFailureException;
|
||||
|
||||
/**
|
||||
* A WebSocket {@code RequestUpgradeStrategy} for Oracle's WebLogic 12.1.3 and higher.
|
||||
* A WebSocket {@code RequestUpgradeStrategy} for Oracle's WebLogic.
|
||||
* Supports 12.1.3 and 12.2.1.0.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeStrategy {
|
||||
|
||||
private static final TyrusEndpointHelper endpointHelper = new Tyrus135EndpointHelper();
|
||||
private static ClassLoader classLoader = WebLogicRequestUpgradeStrategy.class.getClassLoader();
|
||||
|
||||
private static final boolean WLS_12_1_3 = isWebLogic1213();
|
||||
|
||||
private static final TyrusEndpointHelper endpointHelper = WLS_12_1_3 ?
|
||||
new Tyrus135EndpointHelper() : new Tyrus17EndpointHelper();
|
||||
|
||||
private static final TyrusMuxableWebSocketHelper webSocketHelper = new TyrusMuxableWebSocketHelper();
|
||||
|
||||
private static final WebLogicServletWriterHelper servletWriterHelper = new WebLogicServletWriterHelper();
|
||||
|
||||
|
||||
|
||||
private static boolean isWebLogic1213() {
|
||||
try {
|
||||
type("weblogic.websocket.tyrus.TyrusMuxableWebSocket").getDeclaredConstructor(
|
||||
type("weblogic.servlet.internal.MuxableSocketHTTP"));
|
||||
return true;
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException("No compatible WebSocket version found", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TyrusEndpointHelper getEndpointHelper() {
|
||||
return endpointHelper;
|
||||
|
|
@ -76,7 +101,7 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
Object nativeRequest = getNativeRequest(request);
|
||||
BeanWrapper beanWrapper = new BeanWrapperImpl(nativeRequest);
|
||||
Object httpSocket = beanWrapper.getPropertyValue("connection.connectionHandler.rawConnection");
|
||||
Object webSocket = webSocketHelper.newInstance(httpSocket);
|
||||
Object webSocket = webSocketHelper.newInstance(request, httpSocket);
|
||||
webSocketHelper.upgrade(webSocket, httpSocket, request.getServletContext());
|
||||
|
||||
response.flushBuffer();
|
||||
|
|
@ -96,6 +121,16 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
return request;
|
||||
}
|
||||
|
||||
private static Class<?> type(String className) throws ClassNotFoundException {
|
||||
return classLoader.loadClass(className);
|
||||
}
|
||||
|
||||
private static Method method(String className, String method, Class<?>... paramTypes)
|
||||
throws ClassNotFoundException, NoSuchMethodException {
|
||||
|
||||
return type(className).getDeclaredMethod(method, paramTypes);
|
||||
}
|
||||
|
||||
|
||||
private static final Connection.CloseListener noOpCloseListener = new Connection.CloseListener() {
|
||||
|
||||
|
|
@ -110,33 +145,47 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
*/
|
||||
private static class TyrusMuxableWebSocketHelper {
|
||||
|
||||
public static final Class<?> webSocketType;
|
||||
private static final Class<?> type;
|
||||
|
||||
private static final Constructor<?> webSocketConstructor;
|
||||
private static final Constructor<?> constructor;
|
||||
|
||||
private static final Method webSocketUpgradeMethod;
|
||||
private static final SubjectHelper subjectHelper;
|
||||
|
||||
private static final Method webSocketReadEventMethod;
|
||||
private static final Method upgradeMethod;
|
||||
|
||||
private static final Method readEventMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
ClassLoader classLoader = WebLogicRequestUpgradeStrategy.class.getClassLoader();
|
||||
Class<?> socketType = classLoader.loadClass("weblogic.socket.MuxableSocket");
|
||||
Class<?> httpSocketType = classLoader.loadClass("weblogic.servlet.internal.MuxableSocketHTTP");
|
||||
type = type("weblogic.websocket.tyrus.TyrusMuxableWebSocket");
|
||||
if (WLS_12_1_3) {
|
||||
constructor = type.getDeclaredConstructor(type("weblogic.servlet.internal.MuxableSocketHTTP"));
|
||||
subjectHelper = null;
|
||||
}
|
||||
else {
|
||||
constructor = type.getDeclaredConstructor(
|
||||
type("weblogic.servlet.internal.MuxableSocketHTTP"),
|
||||
type("weblogic.websocket.tyrus.CoherenceServletFilterService"),
|
||||
type("weblogic.servlet.spi.SubjectHandle"));
|
||||
subjectHelper = new SubjectHelper();
|
||||
}
|
||||
|
||||
webSocketType = classLoader.loadClass("weblogic.websocket.tyrus.TyrusMuxableWebSocket");
|
||||
webSocketConstructor = webSocketType.getDeclaredConstructor(httpSocketType);
|
||||
webSocketUpgradeMethod = webSocketType.getMethod("upgrade", socketType, ServletContext.class);
|
||||
webSocketReadEventMethod = webSocketType.getMethod("registerForReadEvent");
|
||||
upgradeMethod = type.getMethod("upgrade", type("weblogic.socket.MuxableSocket"), ServletContext.class);
|
||||
|
||||
readEventMethod = type.getMethod("registerForReadEvent");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("No compatible WebSocket version found", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Object newInstance(Object httpSocket) {
|
||||
private Object newInstance(HttpServletRequest request, Object httpSocket) {
|
||||
try {
|
||||
return webSocketConstructor.newInstance(httpSocket);
|
||||
Object[] args = (WLS_12_1_3 ?
|
||||
new Object[] {httpSocket} :
|
||||
new Object[] {httpSocket, null, subjectHelper.getSubject(request)});
|
||||
|
||||
return constructor.newInstance(args);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to create TyrusMuxableWebSocket", ex);
|
||||
|
|
@ -145,7 +194,7 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
|
||||
private void upgrade(Object webSocket, Object httpSocket, ServletContext servletContext) {
|
||||
try {
|
||||
webSocketUpgradeMethod.invoke(webSocket, httpSocket, servletContext);
|
||||
upgradeMethod.invoke(webSocket, httpSocket, servletContext);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to upgrade TyrusMuxableWebSocket", ex);
|
||||
|
|
@ -154,7 +203,7 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
|
||||
private void registerForReadEvent(Object webSocket) {
|
||||
try {
|
||||
webSocketReadEventMethod.invoke(webSocket);
|
||||
readEventMethod.invoke(webSocket);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to register WebSocket for read event", ex);
|
||||
|
|
@ -162,6 +211,52 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
}
|
||||
}
|
||||
|
||||
private static class SubjectHelper {
|
||||
|
||||
private final Method securityContextMethod;
|
||||
|
||||
private final Method currentUserMethod;
|
||||
|
||||
private final Method providerMethod;
|
||||
|
||||
private final Method anonymousSubjectMethod;
|
||||
|
||||
|
||||
public SubjectHelper() {
|
||||
try {
|
||||
String className = "weblogic.servlet.internal.WebAppServletContext";
|
||||
securityContextMethod = method(className, "getSecurityContext");
|
||||
|
||||
className = "weblogic.servlet.security.internal.SecurityModule";
|
||||
currentUserMethod = method(className, "getCurrentUser",
|
||||
type("weblogic.servlet.security.internal.ServletSecurityContext"),
|
||||
HttpServletRequest.class);
|
||||
|
||||
className = "weblogic.servlet.security.internal.WebAppSecurity";
|
||||
providerMethod = method(className, "getProvider");
|
||||
anonymousSubjectMethod = providerMethod.getReturnType().getDeclaredMethod("getAnonymousSubject");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("No compatible WebSocket version found", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getSubject(HttpServletRequest request) {
|
||||
try {
|
||||
ServletContext servletContext = request.getServletContext();
|
||||
Object securityContext = securityContextMethod.invoke(servletContext);
|
||||
Object subject = currentUserMethod.invoke(null, securityContext, request);
|
||||
if (subject == null) {
|
||||
Object securityProvider = providerMethod.invoke(null);
|
||||
subject = anonymousSubjectMethod.invoke(securityProvider);
|
||||
}
|
||||
return subject;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to obtain SubjectHandle", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps to create and invoke {@code weblogic.websocket.tyrus.TyrusServletWriter}.
|
||||
|
|
@ -172,12 +267,16 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
|
||||
static {
|
||||
try {
|
||||
ClassLoader loader = WebLogicRequestUpgradeStrategy.class.getClassLoader();
|
||||
Class<?> type = loader.loadClass("weblogic.websocket.tyrus.TyrusServletWriter");
|
||||
Class<?> listenerType = loader.loadClass("weblogic.websocket.tyrus.TyrusServletWriter$CloseListener");
|
||||
Class<?> webSocketType = TyrusMuxableWebSocketHelper.webSocketType;
|
||||
Class<?> writerType = type("weblogic.websocket.tyrus.TyrusServletWriter");
|
||||
Class<?> listenerType = type("weblogic.websocket.tyrus.TyrusServletWriter$CloseListener");
|
||||
Class<?> webSocketType = TyrusMuxableWebSocketHelper.type;
|
||||
Class<HttpServletResponse> responseType = HttpServletResponse.class;
|
||||
constructor = type.getDeclaredConstructor(webSocketType, responseType, listenerType, boolean.class);
|
||||
|
||||
Class<?>[] argTypes = (WLS_12_1_3 ?
|
||||
new Class<?>[] {webSocketType, responseType, listenerType, boolean.class} :
|
||||
new Class<?>[] {webSocketType, listenerType, boolean.class});
|
||||
|
||||
constructor = writerType.getDeclaredConstructor(argTypes);
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
|
@ -187,7 +286,11 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
|
||||
private Writer newInstance(HttpServletResponse response, Object webSocket, boolean isProtected) {
|
||||
try {
|
||||
return (Writer) constructor.newInstance(webSocket, response, null, isProtected);
|
||||
Object[] args = (WLS_12_1_3 ?
|
||||
new Object[] {webSocket, response, null, isProtected} :
|
||||
new Object[] {webSocket, null, isProtected});
|
||||
|
||||
return (Writer) constructor.newInstance(args);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to create TyrusServletWriter", ex);
|
||||
|
|
|
|||
Loading…
Reference in New Issue