Require Undertow 1.3.5+, Tyrus 1.11+, Jetty 9.3+, Tomcat 8.5+
Issue: SPR-13495
This commit is contained in:
parent
ccf791b63f
commit
770f0c0661
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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.apache.catalina.loader;
|
||||
|
||||
/**
|
||||
* A mock of Tomcat's {@code WebappClassLoader} just for Spring's compilation purposes.
|
||||
* Exposes both pre-7.0.63 as well as 7.0.63+ variants of {@code findResourceInternal}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.2
|
||||
*/
|
||||
public class WebappClassLoader extends ClassLoader {
|
||||
|
||||
public WebappClassLoader() {
|
||||
}
|
||||
|
||||
public WebappClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
|
||||
protected ResourceEntry findResourceInternal(String name, String path) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected ResourceEntry findResourceInternal(String name, String path, boolean manifestRequired) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.instrument.classloading;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ClassFileTransformer-based weaver, allowing for a list of transformers to be
|
||||
* applied on a class byte array. Normally used inside class loaders.
|
||||
*
|
||||
* <p>Note: This class is deliberately implemented for minimal external dependencies,
|
||||
* since it is included in weaver jars (to be deployed into application servers).
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class WeavingTransformer {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final List<ClassFileTransformer> transformers = new ArrayList<ClassFileTransformer>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new WeavingTransformer for the given class loader.
|
||||
* @param classLoader the ClassLoader to build a transformer for
|
||||
*/
|
||||
public WeavingTransformer(ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
throw new IllegalArgumentException("ClassLoader must not be null");
|
||||
}
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a class file transformer to be applied by this weaver.
|
||||
* @param transformer the class file transformer to register
|
||||
*/
|
||||
public void addTransformer(ClassFileTransformer transformer) {
|
||||
if (transformer == null) {
|
||||
throw new IllegalArgumentException("Transformer must not be null");
|
||||
}
|
||||
this.transformers.add(transformer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply transformation on a given class byte definition.
|
||||
* The method will always return a non-null byte array (if no transformation has taken place
|
||||
* the array content will be identical to the original one).
|
||||
* @param className the full qualified name of the class in dot format (i.e. some.package.SomeClass)
|
||||
* @param bytes class byte definition
|
||||
* @return (possibly transformed) class byte definition
|
||||
*/
|
||||
public byte[] transformIfNecessary(String className, byte[] bytes) {
|
||||
String internalName = className.replace(".", "/");
|
||||
return transformIfNecessary(className, internalName, bytes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply transformation on a given class byte definition.
|
||||
* The method will always return a non-null byte array (if no transformation has taken place
|
||||
* the array content will be identical to the original one).
|
||||
* @param className the full qualified name of the class in dot format (i.e. some.package.SomeClass)
|
||||
* @param internalName class name internal name in / format (i.e. some/package/SomeClass)
|
||||
* @param bytes class byte definition
|
||||
* @param pd protection domain to be used (can be null)
|
||||
* @return (possibly transformed) class byte definition
|
||||
*/
|
||||
public byte[] transformIfNecessary(String className, String internalName, byte[] bytes, ProtectionDomain pd) {
|
||||
byte[] result = bytes;
|
||||
for (ClassFileTransformer cft : this.transformers) {
|
||||
try {
|
||||
byte[] transformed = cft.transform(this.classLoader, internalName, null, pd, result);
|
||||
if (transformed != null) {
|
||||
result = transformed;
|
||||
}
|
||||
}
|
||||
catch (IllegalClassFormatException ex) {
|
||||
throw new IllegalStateException("Class file transformation failed", ex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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.springframework.instrument.classloading.tomcat;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.apache.catalina.loader.ResourceEntry;
|
||||
import org.apache.catalina.loader.WebappClassLoader;
|
||||
|
||||
import org.springframework.instrument.classloading.WeavingTransformer;
|
||||
|
||||
/**
|
||||
* Extension of Tomcat's default class loader which adds instrumentation
|
||||
* to loaded classes without the need to use a VM-wide agent.
|
||||
*
|
||||
* <p>To be registered using a
|
||||
* <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/loader.html">{@code Loader}</a> tag
|
||||
* in Tomcat's <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/context.html">{@code Context}</a>
|
||||
* definition in the {@code server.xml} file, with the Spring-provided "spring-instrument-tomcat.jar"
|
||||
* file deployed into Tomcat's "lib" directory. The required configuration tag looks as follows:
|
||||
*
|
||||
* <pre class="code"><Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/></pre>
|
||||
*
|
||||
* <p>Typically used in combination with a
|
||||
* {@link org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver}
|
||||
* defined in the Spring application context. The {@code addTransformer} and
|
||||
* {@code getThrowawayClassLoader} methods mirror the corresponding methods
|
||||
* in the LoadTimeWeaver interface, as expected by ReflectiveLoadTimeWeaver.
|
||||
*
|
||||
* <p><b>NOTE:</b> Requires Apache Tomcat version 6.0 or higher, as of Spring 4.0.
|
||||
* This class is not intended to work on Tomcat 8.0+; please rely on Tomcat's own
|
||||
* {@code InstrumentableClassLoader} facility instead, as autodetected by Spring's
|
||||
* {@link org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see #addTransformer
|
||||
* @see #getThrowawayClassLoader
|
||||
* @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
|
||||
* @see org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver
|
||||
*/
|
||||
public class TomcatInstrumentableClassLoader extends WebappClassLoader {
|
||||
|
||||
private static final String CLASS_SUFFIX = ".class";
|
||||
|
||||
/** Use an internal WeavingTransformer */
|
||||
private final WeavingTransformer weavingTransformer;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code TomcatInstrumentableClassLoader} using the
|
||||
* current context class loader.
|
||||
* @see #TomcatInstrumentableClassLoader(ClassLoader)
|
||||
*/
|
||||
public TomcatInstrumentableClassLoader() {
|
||||
super();
|
||||
this.weavingTransformer = new WeavingTransformer(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code TomcatInstrumentableClassLoader} with the
|
||||
* supplied class loader as parent.
|
||||
* @param parent the parent {@link ClassLoader} to be used
|
||||
*/
|
||||
public TomcatInstrumentableClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
this.weavingTransformer = new WeavingTransformer(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegate for LoadTimeWeaver's {@code addTransformer} method.
|
||||
* Typically called through ReflectiveLoadTimeWeaver.
|
||||
* @see org.springframework.instrument.classloading.LoadTimeWeaver#addTransformer
|
||||
* @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
|
||||
*/
|
||||
public void addTransformer(ClassFileTransformer transformer) {
|
||||
this.weavingTransformer.addTransformer(transformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for LoadTimeWeaver's {@code getThrowawayClassLoader} method.
|
||||
* Typically called through ReflectiveLoadTimeWeaver.
|
||||
* @see org.springframework.instrument.classloading.LoadTimeWeaver#getThrowawayClassLoader
|
||||
* @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
|
||||
*/
|
||||
public ClassLoader getThrowawayClassLoader() {
|
||||
WebappClassLoader tempLoader = new WebappClassLoader();
|
||||
// Use reflection to copy all the fields since they are not exposed any other way.
|
||||
shallowCopyFieldState(this, tempLoader);
|
||||
return tempLoader;
|
||||
}
|
||||
|
||||
|
||||
@Override // overriding the pre-7.0.63 variant of findResourceInternal
|
||||
protected ResourceEntry findResourceInternal(String name, String path) {
|
||||
ResourceEntry entry = super.findResourceInternal(name, path);
|
||||
if (entry != null && entry.binaryContent != null && path.endsWith(CLASS_SUFFIX)) {
|
||||
String className = (name.endsWith(CLASS_SUFFIX) ? name.substring(0, name.length() - CLASS_SUFFIX.length()) : name);
|
||||
entry.binaryContent = this.weavingTransformer.transformIfNecessary(className, entry.binaryContent);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override // overriding the 7.0.63+ variant of findResourceInternal
|
||||
protected ResourceEntry findResourceInternal(String name, String path, boolean manifestRequired) {
|
||||
ResourceEntry entry = super.findResourceInternal(name, path, manifestRequired);
|
||||
if (entry != null && entry.binaryContent != null && path.endsWith(CLASS_SUFFIX)) {
|
||||
String className = (name.endsWith(CLASS_SUFFIX) ? name.substring(0, name.length() - CLASS_SUFFIX.length()) : name);
|
||||
entry.binaryContent = this.weavingTransformer.transformIfNecessary(className, entry.binaryContent);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + "\r\n" + super.toString();
|
||||
}
|
||||
|
||||
|
||||
// The code below is originally taken from ReflectionUtils and optimized for
|
||||
// local usage. There is no dependency on ReflectionUtils to keep this class
|
||||
// self-contained (since it gets deployed into Tomcat's server class loader).
|
||||
private static void shallowCopyFieldState(final WebappClassLoader src, final WebappClassLoader dest) {
|
||||
Class<?> targetClass = WebappClassLoader.class;
|
||||
// Keep backing up the inheritance hierarchy.
|
||||
do {
|
||||
Field[] fields = targetClass.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
// Do not copy resourceEntries - it's a cache that holds class entries.
|
||||
if (!(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()) ||
|
||||
field.getName().equals("resourceEntries"))) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
Object srcValue = field.get(src);
|
||||
field.set(dest, srcValue);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Shouldn't be illegal to access field '" + field.getName() + "': " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>
|
||||
Spring's instrumentation agent for Tomcat.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
log4j.appender.console=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.console.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%c] - %m%n
|
||||
|
||||
log4j.rootCategory=WARN, console
|
||||
log4j.logger.org.springframework.beans=WARN
|
||||
log4j.logger.org.springframework.binding=DEBUG
|
||||
|
||||
#log4j.logger.org.springframework.instrument.classloading=TRACE
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -56,8 +56,8 @@ import org.springframework.web.socket.server.HandshakeFailureException;
|
|||
import org.springframework.web.socket.server.RequestUpgradeStrategy;
|
||||
|
||||
/**
|
||||
* A {@link RequestUpgradeStrategy} for use with Jetty 9.0-9.3. Based on Jetty's
|
||||
* internal {@code org.eclipse.jetty.websocket.server.WebSocketHandler} class.
|
||||
* A {@link RequestUpgradeStrategy} for use with Jetty 9.3 and higher. Based on
|
||||
* Jetty's internal {@code org.eclipse.jetty.websocket.server.WebSocketHandler} class.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Rossen Stoyanchev
|
||||
|
|
@ -65,10 +65,6 @@ import org.springframework.web.socket.server.RequestUpgradeStrategy;
|
|||
*/
|
||||
public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Lifecycle, ServletContextAware {
|
||||
|
||||
// Pre-Jetty 9.3 init method without ServletContext
|
||||
private static final Method webSocketFactoryInitMethod =
|
||||
ClassUtils.getMethodIfAvailable(WebSocketServerFactory.class, "init");
|
||||
|
||||
private static final ThreadLocal<WebSocketHandlerContainer> wsContainerHolder =
|
||||
new NamedThreadLocal<WebSocketHandlerContainer>("WebSocket Handler Container");
|
||||
|
||||
|
|
@ -153,12 +149,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
|||
if (!isRunning()) {
|
||||
this.running = true;
|
||||
try {
|
||||
if (webSocketFactoryInitMethod != null) {
|
||||
webSocketFactoryInitMethod.invoke(this.factory);
|
||||
}
|
||||
else {
|
||||
this.factory.init(this.servletContext);
|
||||
}
|
||||
this.factory.init(this.servletContext);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to initialize Jetty WebSocketServerFactory", ex);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -35,12 +35,10 @@ import javax.websocket.WebSocketContainer;
|
|||
|
||||
import org.glassfish.tyrus.core.ComponentProviderService;
|
||||
import org.glassfish.tyrus.core.RequestContext;
|
||||
import org.glassfish.tyrus.core.TyrusEndpoint;
|
||||
import org.glassfish.tyrus.core.TyrusEndpointWrapper;
|
||||
import org.glassfish.tyrus.core.TyrusUpgradeResponse;
|
||||
import org.glassfish.tyrus.core.TyrusWebSocketEngine;
|
||||
import org.glassfish.tyrus.core.Version;
|
||||
import org.glassfish.tyrus.core.WebSocketApplication;
|
||||
import org.glassfish.tyrus.server.TyrusServerContainer;
|
||||
import org.glassfish.tyrus.spi.WebSocketEngine.UpgradeInfo;
|
||||
|
||||
|
|
@ -59,11 +57,11 @@ import static org.glassfish.tyrus.spi.WebSocketEngine.UpgradeStatus.*;
|
|||
* A base class for {@code RequestUpgradeStrategy} implementations on top of
|
||||
* JSR-356 based servers which include Tyrus as their WebSocket engine.
|
||||
*
|
||||
* <p>Works with Tyrus 1.3.5 (WebLogic 12.1.3), Tyrus 1.7 (GlassFish 4.1.0),
|
||||
* Tyrus 1.11 (WebLogic 12.2.1), and Tyrus 1.12 (GlassFish 4.1.1).
|
||||
* <p>Works with Tyrus 1.11 (WebLogic 12.2.1) and Tyrus 1.12 (GlassFish 4.1.1).
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
* @see <a href="https://tyrus.java.net/">Project Tyrus</a>
|
||||
*/
|
||||
|
|
@ -71,6 +69,42 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
|
||||
private static final Random random = new Random();
|
||||
|
||||
private static final Constructor<?> constructor;
|
||||
|
||||
private static boolean constructorWithBooleanArgument;
|
||||
|
||||
private static final Method registerMethod;
|
||||
|
||||
private static final Method unRegisterMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
constructor = getEndpointConstructor();
|
||||
int parameterCount = constructor.getParameterTypes().length;
|
||||
constructorWithBooleanArgument = (parameterCount == 10);
|
||||
if (!constructorWithBooleanArgument && parameterCount != 9) {
|
||||
throw new IllegalStateException("Expected TyrusEndpointWrapper constructor with 9 or 10 arguments");
|
||||
}
|
||||
registerMethod = TyrusWebSocketEngine.class.getDeclaredMethod("register", TyrusEndpointWrapper.class);
|
||||
unRegisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class);
|
||||
ReflectionUtils.makeAccessible(registerMethod);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("No compatible Tyrus version found", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Constructor<?> getEndpointConstructor() {
|
||||
for (Constructor<?> current : TyrusEndpointWrapper.class.getConstructors()) {
|
||||
Class<?>[] types = current.getParameterTypes();
|
||||
if (Endpoint.class == types[0] && EndpointConfig.class == types[1]) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No compatible Tyrus version found");
|
||||
}
|
||||
|
||||
|
||||
private final ComponentProviderService componentProvider = ComponentProviderService.create();
|
||||
|
||||
|
||||
|
|
@ -105,7 +139,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
// Shouldn't matter for processing but must be unique
|
||||
String path = "/" + random.nextLong();
|
||||
tyrusEndpoint = createTyrusEndpoint(endpoint, path, selectedProtocol, extensions, serverContainer, engine);
|
||||
getEndpointHelper().register(engine, tyrusEndpoint);
|
||||
register(engine, tyrusEndpoint);
|
||||
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
RequestContext requestContext = createRequestContext(servletRequest, path, headers);
|
||||
|
|
@ -137,7 +171,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(endpointPath, endpoint);
|
||||
endpointConfig.setSubprotocols(Collections.singletonList(protocol));
|
||||
endpointConfig.setExtensions(extensions);
|
||||
return getEndpointHelper().createdEndpoint(endpointConfig, this.componentProvider, container, engine);
|
||||
return createEndpoint(endpointConfig, this.componentProvider, container, engine);
|
||||
}
|
||||
|
||||
private RequestContext createRequestContext(HttpServletRequest request, String endpointPath, HttpHeaders headers) {
|
||||
|
|
@ -146,7 +180,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
.requestURI(URI.create(endpointPath))
|
||||
.userPrincipal(request.getUserPrincipal())
|
||||
.secure(request.isSecure())
|
||||
// .remoteAddr(request.getRemoteAddr()) # Not available in 1.3.5
|
||||
.remoteAddr(request.getRemoteAddr())
|
||||
.build();
|
||||
for (String header : headers.keySet()) {
|
||||
context.getHeaders().put(header, headers.get(header));
|
||||
|
|
@ -157,7 +191,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
private void unregisterTyrusEndpoint(TyrusWebSocketEngine engine, Object tyrusEndpoint) {
|
||||
if (tyrusEndpoint != null) {
|
||||
try {
|
||||
getEndpointHelper().unregister(engine, tyrusEndpoint);
|
||||
unregister(engine, tyrusEndpoint);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// ignore
|
||||
|
|
@ -165,147 +199,48 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract TyrusEndpointHelper getEndpointHelper();
|
||||
private Object createEndpoint(ServerEndpointRegistration registration, ComponentProviderService provider,
|
||||
WebSocketContainer container, TyrusWebSocketEngine engine) throws DeploymentException {
|
||||
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(engine);
|
||||
Object sessionListener = accessor.getPropertyValue("sessionListener");
|
||||
Object clusterContext = accessor.getPropertyValue("clusterContext");
|
||||
try {
|
||||
if (constructorWithBooleanArgument) {
|
||||
// Tyrus 1.11+
|
||||
return constructor.newInstance(registration.getEndpoint(), registration, provider, container,
|
||||
"/", registration.getConfigurator(), sessionListener, clusterContext, null, Boolean.TRUE);
|
||||
}
|
||||
else {
|
||||
return constructor.newInstance(registration.getEndpoint(), registration, provider, container,
|
||||
"/", registration.getConfigurator(), sessionListener, clusterContext, null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to register " + registration, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void register(TyrusWebSocketEngine engine, Object endpoint) {
|
||||
try {
|
||||
registerMethod.invoke(engine, endpoint);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to register " + endpoint, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void unregister(TyrusWebSocketEngine engine, Object endpoint) {
|
||||
try {
|
||||
unRegisterMethod.invoke(engine, endpoint);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to unregister " + endpoint, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected abstract void handleSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
UpgradeInfo upgradeInfo, TyrusUpgradeResponse upgradeResponse) throws IOException, ServletException;
|
||||
|
||||
|
||||
/**
|
||||
* Helps with the creation, registration, and un-registration of endpoints.
|
||||
*/
|
||||
protected interface TyrusEndpointHelper {
|
||||
|
||||
Object createdEndpoint(ServerEndpointRegistration registration, ComponentProviderService provider,
|
||||
WebSocketContainer container, TyrusWebSocketEngine engine) throws DeploymentException;
|
||||
|
||||
void register(TyrusWebSocketEngine engine, Object endpoint);
|
||||
|
||||
void unregister(TyrusWebSocketEngine engine, Object endpoint);
|
||||
}
|
||||
|
||||
|
||||
protected static class Tyrus17EndpointHelper implements TyrusEndpointHelper {
|
||||
|
||||
private static final Constructor<?> constructor;
|
||||
|
||||
private static boolean constructorWithBooleanArgument;
|
||||
|
||||
private static final Method registerMethod;
|
||||
|
||||
private static final Method unRegisterMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
constructor = getEndpointConstructor();
|
||||
int parameterCount = constructor.getParameterTypes().length;
|
||||
constructorWithBooleanArgument = (parameterCount == 10);
|
||||
if (!constructorWithBooleanArgument && parameterCount != 9) {
|
||||
throw new IllegalStateException("Expected TyrusEndpointWrapper constructor with 9 or 10 arguments");
|
||||
}
|
||||
registerMethod = TyrusWebSocketEngine.class.getDeclaredMethod("register", TyrusEndpointWrapper.class);
|
||||
unRegisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class);
|
||||
ReflectionUtils.makeAccessible(registerMethod);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("No compatible Tyrus version found", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Constructor<?> getEndpointConstructor() {
|
||||
for (Constructor<?> current : TyrusEndpointWrapper.class.getConstructors()) {
|
||||
Class<?>[] types = current.getParameterTypes();
|
||||
if (Endpoint.class == types[0] && EndpointConfig.class == types[1]) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No compatible Tyrus version found");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object createdEndpoint(ServerEndpointRegistration registration, ComponentProviderService provider,
|
||||
WebSocketContainer container, TyrusWebSocketEngine engine) throws DeploymentException {
|
||||
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(engine);
|
||||
Object sessionListener = accessor.getPropertyValue("sessionListener");
|
||||
Object clusterContext = accessor.getPropertyValue("clusterContext");
|
||||
try {
|
||||
if (constructorWithBooleanArgument) {
|
||||
// Tyrus 1.11+
|
||||
return constructor.newInstance(registration.getEndpoint(), registration, provider, container,
|
||||
"/", registration.getConfigurator(), sessionListener, clusterContext, null, Boolean.TRUE);
|
||||
}
|
||||
else {
|
||||
return constructor.newInstance(registration.getEndpoint(), registration, provider, container,
|
||||
"/", registration.getConfigurator(), sessionListener, clusterContext, null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to register " + registration, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(TyrusWebSocketEngine engine, Object endpoint) {
|
||||
try {
|
||||
registerMethod.invoke(engine, endpoint);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to register " + endpoint, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(TyrusWebSocketEngine engine, Object endpoint) {
|
||||
try {
|
||||
unRegisterMethod.invoke(engine, endpoint);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to unregister " + endpoint, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static class Tyrus135EndpointHelper implements TyrusEndpointHelper {
|
||||
|
||||
private static final Method registerMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
registerMethod = TyrusWebSocketEngine.class.getDeclaredMethod("register", WebSocketApplication.class);
|
||||
ReflectionUtils.makeAccessible(registerMethod);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("No compatible Tyrus version found", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createdEndpoint(ServerEndpointRegistration registration, ComponentProviderService provider,
|
||||
WebSocketContainer container, TyrusWebSocketEngine engine) throws DeploymentException {
|
||||
|
||||
TyrusEndpointWrapper endpointWrapper = new TyrusEndpointWrapper(registration.getEndpoint(),
|
||||
registration, provider, container, "/", registration.getConfigurator());
|
||||
|
||||
return new TyrusEndpoint(endpointWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(TyrusWebSocketEngine engine, Object endpoint) {
|
||||
try {
|
||||
registerMethod.invoke(engine, endpoint);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to register " + endpoint, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(TyrusWebSocketEngine engine, Object endpoint) {
|
||||
engine.unregister((TyrusEndpoint) endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -43,22 +43,27 @@ import org.springframework.web.socket.server.HandshakeFailureException;
|
|||
*/
|
||||
public class GlassFishRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeStrategy {
|
||||
|
||||
private static final TyrusEndpointHelper endpointHelper = new Tyrus17EndpointHelper();
|
||||
private static final Constructor<?> constructor;
|
||||
|
||||
private static final GlassFishServletWriterHelper servletWriterHelper = new GlassFishServletWriterHelper();
|
||||
|
||||
|
||||
@Override
|
||||
protected TyrusEndpointHelper getEndpointHelper() {
|
||||
return endpointHelper;
|
||||
static {
|
||||
try {
|
||||
ClassLoader classLoader = GlassFishRequestUpgradeStrategy.class.getClassLoader();
|
||||
Class<?> type = classLoader.loadClass("org.glassfish.tyrus.servlet.TyrusServletWriter");
|
||||
constructor = type.getDeclaredConstructor(TyrusHttpUpgradeHandler.class);
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("No compatible Tyrus version found", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
UpgradeInfo upgradeInfo, TyrusUpgradeResponse upgradeResponse) throws IOException, ServletException {
|
||||
|
||||
TyrusHttpUpgradeHandler handler = request.upgrade(TyrusHttpUpgradeHandler.class);
|
||||
Writer servletWriter = servletWriterHelper.newInstance(handler);
|
||||
Writer servletWriter = newServletWriter(handler);
|
||||
handler.preInit(upgradeInfo, servletWriter, request.getUserPrincipal() != null);
|
||||
|
||||
response.setStatus(upgradeResponse.getStatus());
|
||||
|
|
@ -68,33 +73,12 @@ public class GlassFishRequestUpgradeStrategy extends AbstractTyrusRequestUpgrade
|
|||
response.flushBuffer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helps to create and invoke {@code org.glassfish.tyrus.servlet.TyrusServletWriter}.
|
||||
*/
|
||||
private static class GlassFishServletWriterHelper {
|
||||
|
||||
private static final Constructor<?> constructor;
|
||||
|
||||
static {
|
||||
try {
|
||||
ClassLoader classLoader = GlassFishRequestUpgradeStrategy.class.getClassLoader();
|
||||
Class<?> type = classLoader.loadClass("org.glassfish.tyrus.servlet.TyrusServletWriter");
|
||||
constructor = type.getDeclaredConstructor(TyrusHttpUpgradeHandler.class);
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("No compatible Tyrus version found", ex);
|
||||
}
|
||||
private Writer newServletWriter(TyrusHttpUpgradeHandler handler) {
|
||||
try {
|
||||
return (Writer) constructor.newInstance(handler);
|
||||
}
|
||||
|
||||
private Writer newInstance(TyrusHttpUpgradeHandler handler) {
|
||||
try {
|
||||
return (Writer) constructor.newInstance(handler);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to instantiate TyrusServletWriter", ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to instantiate TyrusServletWriter", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -17,66 +17,33 @@
|
|||
package org.springframework.web.socket.server.standard;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.websocket.Decoder;
|
||||
import javax.websocket.Encoder;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.Extension;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.HttpUpgradeListener;
|
||||
import io.undertow.servlet.api.InstanceFactory;
|
||||
import io.undertow.servlet.api.InstanceHandle;
|
||||
import io.undertow.servlet.websockets.ServletWebSocketHttpExchange;
|
||||
import io.undertow.util.PathTemplate;
|
||||
import io.undertow.websockets.core.WebSocketChannel;
|
||||
import io.undertow.websockets.core.WebSocketVersion;
|
||||
import io.undertow.websockets.core.protocol.Handshake;
|
||||
import io.undertow.websockets.jsr.ConfiguredServerEndpoint;
|
||||
import io.undertow.websockets.jsr.EncodingFactory;
|
||||
import io.undertow.websockets.jsr.EndpointSessionHandler;
|
||||
import io.undertow.websockets.jsr.ServerWebSocketContainer;
|
||||
import io.undertow.websockets.jsr.annotated.AnnotatedEndpointFactory;
|
||||
import io.undertow.websockets.jsr.handshake.HandshakeUtil;
|
||||
import io.undertow.websockets.jsr.handshake.JsrHybi07Handshake;
|
||||
import io.undertow.websockets.jsr.handshake.JsrHybi08Handshake;
|
||||
import io.undertow.websockets.jsr.handshake.JsrHybi13Handshake;
|
||||
import io.undertow.websockets.spi.WebSocketHttpExchange;
|
||||
import org.xnio.StreamConnection;
|
||||
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.socket.server.HandshakeFailureException;
|
||||
|
||||
/**
|
||||
* A WebSocket {@code RequestUpgradeStrategy} for WildFly and its underlying
|
||||
* Undertow web server. Also compatible with embedded Undertow usage.
|
||||
*
|
||||
* <p>Designed for Undertow 1.3.5+ as of Spring Framework 4.3, with a fallback
|
||||
* strategy for Undertow 1.0 to 1.3 - as included in WildFly 8.x, 9 and 10.
|
||||
* <p>Requires Undertow 1.3.5+ as of Spring Framework 5.0.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public class UndertowRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy {
|
||||
|
||||
private static final boolean HAS_DO_UPGRADE = ClassUtils.hasMethod(ServerWebSocketContainer.class, "doUpgrade",
|
||||
HttpServletRequest.class, HttpServletResponse.class, ServerEndpointConfig.class, Map.class);
|
||||
|
||||
private static final FallbackStrategy FALLBACK_STRATEGY = (HAS_DO_UPGRADE ? null : new FallbackStrategy());
|
||||
|
||||
private static final String[] VERSIONS = new String[] {
|
||||
WebSocketVersion.V13.toHttpHeaderValue(),
|
||||
WebSocketVersion.V08.toHttpHeaderValue(),
|
||||
|
|
@ -94,32 +61,27 @@ public class UndertowRequestUpgradeStrategy extends AbstractStandardUpgradeStrat
|
|||
String selectedProtocol, List<Extension> selectedExtensions, Endpoint endpoint)
|
||||
throws HandshakeFailureException {
|
||||
|
||||
if (HAS_DO_UPGRADE) {
|
||||
HttpServletRequest servletRequest = getHttpServletRequest(request);
|
||||
HttpServletResponse servletResponse = getHttpServletResponse(response);
|
||||
HttpServletRequest servletRequest = getHttpServletRequest(request);
|
||||
HttpServletResponse servletResponse = getHttpServletResponse(response);
|
||||
|
||||
StringBuffer requestUrl = servletRequest.getRequestURL();
|
||||
String path = servletRequest.getRequestURI(); // shouldn't matter
|
||||
Map<String, String> pathParams = Collections.<String, String>emptyMap();
|
||||
StringBuffer requestUrl = servletRequest.getRequestURL();
|
||||
String path = servletRequest.getRequestURI(); // shouldn't matter
|
||||
Map<String, String> pathParams = Collections.<String, String>emptyMap();
|
||||
|
||||
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint);
|
||||
endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol));
|
||||
endpointConfig.setExtensions(selectedExtensions);
|
||||
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint);
|
||||
endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol));
|
||||
endpointConfig.setExtensions(selectedExtensions);
|
||||
|
||||
try {
|
||||
getContainer(servletRequest).doUpgrade(servletRequest, servletResponse, endpointConfig, pathParams);
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw new HandshakeFailureException(
|
||||
"Servlet request failed to upgrade to WebSocket: " + requestUrl, ex);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new HandshakeFailureException(
|
||||
"Response update failed during upgrade to WebSocket: " + requestUrl, ex);
|
||||
}
|
||||
try {
|
||||
getContainer(servletRequest).doUpgrade(servletRequest, servletResponse, endpointConfig, pathParams);
|
||||
}
|
||||
else {
|
||||
FALLBACK_STRATEGY.upgradeInternal(request, response, selectedProtocol, selectedExtensions, endpoint);
|
||||
catch (ServletException ex) {
|
||||
throw new HandshakeFailureException(
|
||||
"Servlet request failed to upgrade to WebSocket: " + requestUrl, ex);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new HandshakeFailureException(
|
||||
"Response update failed during upgrade to WebSocket: " + requestUrl, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,197 +89,4 @@ public class UndertowRequestUpgradeStrategy extends AbstractStandardUpgradeStrat
|
|||
return (ServerWebSocketContainer) super.getContainer(request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Strategy for use with Undertow 1.0 to 1.3 before there was a public API
|
||||
* to perform a WebSocket upgrade.
|
||||
*/
|
||||
private static class FallbackStrategy extends AbstractStandardUpgradeStrategy {
|
||||
|
||||
private static final Constructor<ServletWebSocketHttpExchange> exchangeConstructor;
|
||||
|
||||
private static final boolean exchangeConstructorWithPeerConnections;
|
||||
|
||||
private static final Constructor<ConfiguredServerEndpoint> endpointConstructor;
|
||||
|
||||
private static final boolean endpointConstructorWithEndpointFactory;
|
||||
|
||||
private static final Method getBufferPoolMethod;
|
||||
|
||||
private static final Method createChannelMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<ServletWebSocketHttpExchange> exchangeType = ServletWebSocketHttpExchange.class;
|
||||
Class<?>[] exchangeParamTypes =
|
||||
new Class<?>[] {HttpServletRequest.class, HttpServletResponse.class, Set.class};
|
||||
Constructor<ServletWebSocketHttpExchange> exchangeCtor =
|
||||
ClassUtils.getConstructorIfAvailable(exchangeType, exchangeParamTypes);
|
||||
if (exchangeCtor != null) {
|
||||
// Undertow 1.1+
|
||||
exchangeConstructor = exchangeCtor;
|
||||
exchangeConstructorWithPeerConnections = true;
|
||||
}
|
||||
else {
|
||||
// Undertow 1.0
|
||||
exchangeParamTypes = new Class<?>[] {HttpServletRequest.class, HttpServletResponse.class};
|
||||
exchangeConstructor = exchangeType.getConstructor(exchangeParamTypes);
|
||||
exchangeConstructorWithPeerConnections = false;
|
||||
}
|
||||
|
||||
Class<ConfiguredServerEndpoint> endpointType = ConfiguredServerEndpoint.class;
|
||||
Class<?>[] endpointParamTypes = new Class<?>[] {ServerEndpointConfig.class, InstanceFactory.class,
|
||||
PathTemplate.class, EncodingFactory.class, AnnotatedEndpointFactory.class};
|
||||
Constructor<ConfiguredServerEndpoint> endpointCtor =
|
||||
ClassUtils.getConstructorIfAvailable(endpointType, endpointParamTypes);
|
||||
if (endpointCtor != null) {
|
||||
// Undertow 1.1+
|
||||
endpointConstructor = endpointCtor;
|
||||
endpointConstructorWithEndpointFactory = true;
|
||||
}
|
||||
else {
|
||||
// Undertow 1.0
|
||||
endpointParamTypes = new Class<?>[] {ServerEndpointConfig.class, InstanceFactory.class,
|
||||
PathTemplate.class, EncodingFactory.class};
|
||||
endpointConstructor = endpointType.getConstructor(endpointParamTypes);
|
||||
endpointConstructorWithEndpointFactory = false;
|
||||
}
|
||||
|
||||
// Adapting between different Pool API types in Undertow 1.0-1.2 vs 1.3
|
||||
getBufferPoolMethod = WebSocketHttpExchange.class.getMethod("getBufferPool");
|
||||
createChannelMethod = ReflectionUtils.findMethod(Handshake.class, "createChannel", (Class<?>[]) null);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Incompatible Undertow API version", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private final Set<WebSocketChannel> peerConnections;
|
||||
|
||||
public FallbackStrategy() {
|
||||
if (exchangeConstructorWithPeerConnections) {
|
||||
this.peerConnections = Collections.newSetFromMap(new ConcurrentHashMap<WebSocketChannel, Boolean>());
|
||||
}
|
||||
else {
|
||||
this.peerConnections = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedVersions() {
|
||||
return VERSIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
|
||||
String selectedProtocol, List<Extension> selectedExtensions, final Endpoint endpoint)
|
||||
throws HandshakeFailureException {
|
||||
|
||||
HttpServletRequest servletRequest = getHttpServletRequest(request);
|
||||
HttpServletResponse servletResponse = getHttpServletResponse(response);
|
||||
|
||||
final ServletWebSocketHttpExchange exchange = createHttpExchange(servletRequest, servletResponse);
|
||||
exchange.putAttachment(HandshakeUtil.PATH_PARAMS, Collections.<String, String>emptyMap());
|
||||
|
||||
ServerWebSocketContainer wsContainer = (ServerWebSocketContainer) getContainer(servletRequest);
|
||||
final EndpointSessionHandler endpointSessionHandler = new EndpointSessionHandler(wsContainer);
|
||||
|
||||
final ConfiguredServerEndpoint configuredServerEndpoint = createConfiguredServerEndpoint(
|
||||
selectedProtocol, selectedExtensions, endpoint, servletRequest);
|
||||
|
||||
final Handshake handshake = getHandshakeToUse(exchange, configuredServerEndpoint);
|
||||
|
||||
exchange.upgradeChannel(new HttpUpgradeListener() {
|
||||
@Override
|
||||
public void handleUpgrade(StreamConnection connection, HttpServerExchange serverExchange) {
|
||||
Object bufferPool = ReflectionUtils.invokeMethod(getBufferPoolMethod, exchange);
|
||||
WebSocketChannel channel = (WebSocketChannel) ReflectionUtils.invokeMethod(
|
||||
createChannelMethod, handshake, exchange, connection, bufferPool);
|
||||
if (peerConnections != null) {
|
||||
peerConnections.add(channel);
|
||||
}
|
||||
endpointSessionHandler.onConnect(exchange, channel);
|
||||
}
|
||||
});
|
||||
|
||||
handshake.handshake(exchange);
|
||||
}
|
||||
|
||||
private ServletWebSocketHttpExchange createHttpExchange(HttpServletRequest request, HttpServletResponse response) {
|
||||
try {
|
||||
return (this.peerConnections != null ?
|
||||
exchangeConstructor.newInstance(request, response, this.peerConnections) :
|
||||
exchangeConstructor.newInstance(request, response));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to instantiate ServletWebSocketHttpExchange", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Handshake getHandshakeToUse(ServletWebSocketHttpExchange exchange, ConfiguredServerEndpoint endpoint) {
|
||||
Handshake handshake = new JsrHybi13Handshake(endpoint);
|
||||
if (handshake.matches(exchange)) {
|
||||
return handshake;
|
||||
}
|
||||
handshake = new JsrHybi08Handshake(endpoint);
|
||||
if (handshake.matches(exchange)) {
|
||||
return handshake;
|
||||
}
|
||||
handshake = new JsrHybi07Handshake(endpoint);
|
||||
if (handshake.matches(exchange)) {
|
||||
return handshake;
|
||||
}
|
||||
// Should never occur
|
||||
throw new HandshakeFailureException("No matching Undertow Handshake found: " + exchange.getRequestHeaders());
|
||||
}
|
||||
|
||||
private ConfiguredServerEndpoint createConfiguredServerEndpoint(String selectedProtocol,
|
||||
List<Extension> selectedExtensions, Endpoint endpoint, HttpServletRequest servletRequest) {
|
||||
|
||||
String path = servletRequest.getRequestURI(); // shouldn't matter
|
||||
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration(path, endpoint);
|
||||
endpointRegistration.setSubprotocols(Collections.singletonList(selectedProtocol));
|
||||
endpointRegistration.setExtensions(selectedExtensions);
|
||||
|
||||
EncodingFactory encodingFactory = new EncodingFactory(
|
||||
Collections.<Class<?>, List<InstanceFactory<? extends Encoder>>>emptyMap(),
|
||||
Collections.<Class<?>, List<InstanceFactory<? extends Decoder>>>emptyMap(),
|
||||
Collections.<Class<?>, List<InstanceFactory<? extends Encoder>>>emptyMap(),
|
||||
Collections.<Class<?>, List<InstanceFactory<? extends Decoder>>>emptyMap());
|
||||
try {
|
||||
return (endpointConstructorWithEndpointFactory ?
|
||||
endpointConstructor.newInstance(endpointRegistration,
|
||||
new EndpointInstanceFactory(endpoint), null, encodingFactory, null) :
|
||||
endpointConstructor.newInstance(endpointRegistration,
|
||||
new EndpointInstanceFactory(endpoint), null, encodingFactory));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to instantiate ConfiguredServerEndpoint", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class EndpointInstanceFactory implements InstanceFactory<Endpoint> {
|
||||
|
||||
private final Endpoint endpoint;
|
||||
|
||||
public EndpointInstanceFactory(Endpoint endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceHandle<Endpoint> createInstance() throws InstantiationException {
|
||||
return new InstanceHandle<Endpoint>() {
|
||||
@Override
|
||||
public Endpoint getInstance() {
|
||||
return endpoint;
|
||||
}
|
||||
@Override
|
||||
public void release() {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -46,33 +46,22 @@ import org.springframework.web.socket.server.HandshakeFailureException;
|
|||
* Supports 12.1.3 as well as 12.2.1, as of Spring Framework 4.2.3.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
*/
|
||||
public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeStrategy {
|
||||
|
||||
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 final Connection.CloseListener noOpCloseListener = new Connection.CloseListener() {
|
||||
|
||||
@Override
|
||||
public void close(CloseReason reason) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TyrusEndpointHelper getEndpointHelper() {
|
||||
return endpointHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
UpgradeInfo upgradeInfo, TyrusUpgradeResponse upgradeResponse) throws IOException, ServletException {
|
||||
|
|
@ -94,7 +83,7 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
response.flushBuffer();
|
||||
|
||||
boolean isProtected = request.getUserPrincipal() != null;
|
||||
Writer servletWriter = servletWriterHelper.newInstance(response, webSocket, isProtected);
|
||||
Writer servletWriter = servletWriterHelper.newInstance(webSocket, isProtected);
|
||||
Connection connection = upgradeInfo.createConnection(servletWriter, noOpCloseListener);
|
||||
new BeanWrapperImpl(webSocket).setPropertyValue("connection", connection);
|
||||
new BeanWrapperImpl(servletWriter).setPropertyValue("connection", connection);
|
||||
|
|
@ -102,20 +91,6 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
}
|
||||
|
||||
|
||||
private static boolean isWebLogic1213() {
|
||||
try {
|
||||
type("weblogic.websocket.tyrus.TyrusMuxableWebSocket").getDeclaredConstructor(
|
||||
type("weblogic.servlet.internal.MuxableSocketHTTP"));
|
||||
return true;
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return false;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException("No compatible WebSocket version found", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> type(String className) throws ClassNotFoundException {
|
||||
return WebLogicRequestUpgradeStrategy.class.getClassLoader().loadClass(className);
|
||||
}
|
||||
|
|
@ -153,17 +128,11 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
try {
|
||||
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();
|
||||
}
|
||||
constructor = type.getDeclaredConstructor(
|
||||
type("weblogic.servlet.internal.MuxableSocketHTTP"),
|
||||
type("weblogic.websocket.tyrus.CoherenceServletFilterService"),
|
||||
type("weblogic.servlet.spi.SubjectHandle"));
|
||||
subjectHelper = new SubjectHelper();
|
||||
|
||||
upgradeMethod = type.getMethod("upgrade", type("weblogic.socket.MuxableSocket"), ServletContext.class);
|
||||
readEventMethod = type.getMethod("registerForReadEvent");
|
||||
|
|
@ -175,8 +144,7 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
|
||||
private Object newInstance(HttpServletRequest request, Object httpSocket) {
|
||||
try {
|
||||
Object[] args = (WLS_12_1_3 ? new Object[] {httpSocket} :
|
||||
new Object[] {httpSocket, null, subjectHelper.getSubject(request)});
|
||||
Object[] args = new Object[] {httpSocket, null, subjectHelper.getSubject(request)};
|
||||
return constructor.newInstance(args);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
|
@ -263,13 +231,7 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
Class<?> writerType = type("weblogic.websocket.tyrus.TyrusServletWriter");
|
||||
Class<?> listenerType = type("weblogic.websocket.tyrus.TyrusServletWriter$CloseListener");
|
||||
Class<?> webSocketType = TyrusMuxableWebSocketHelper.type;
|
||||
Class<HttpServletResponse> responseType = HttpServletResponse.class;
|
||||
|
||||
Class<?>[] argTypes = (WLS_12_1_3 ?
|
||||
new Class<?>[] {webSocketType, responseType, listenerType, boolean.class} :
|
||||
new Class<?>[] {webSocketType, listenerType, boolean.class});
|
||||
|
||||
constructor = writerType.getDeclaredConstructor(argTypes);
|
||||
constructor = writerType.getDeclaredConstructor(webSocketType, listenerType, boolean.class);
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
|
@ -277,13 +239,9 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
|
|||
}
|
||||
}
|
||||
|
||||
private Writer newInstance(HttpServletResponse response, Object webSocket, boolean isProtected) {
|
||||
private Writer newInstance(Object webSocket, boolean isProtected) {
|
||||
try {
|
||||
Object[] args = (WLS_12_1_3 ?
|
||||
new Object[] {webSocket, response, null, isProtected} :
|
||||
new Object[] {webSocket, null, isProtected});
|
||||
|
||||
return (Writer) constructor.newInstance(args);
|
||||
return (Writer) constructor.newInstance(webSocket, null, isProtected);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HandshakeFailureException("Failed to create TyrusServletWriter", ex);
|
||||
|
|
|
|||
Loading…
Reference in New Issue