Update WebLogicRequestUpgradeStrategy for WLS 12.2.1

Issue: SPR-13234
This commit is contained in:
Rossen Stoyanchev 2015-10-29 17:08:12 -04:00
parent 1d60a6a6af
commit 3e807c297d
2 changed files with 153 additions and 44 deletions

View File

@ -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();

View File

@ -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);