Polishing

This commit is contained in:
Juergen Hoeller
2015-12-13 23:27:05 +01:00
parent ef1e17fd15
commit bdb606b8b1
7 changed files with 168 additions and 147 deletions

View File

@@ -20,14 +20,15 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import reactor.fn.Consumer;
import reactor.rx.Promise;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.FailureCallback;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.util.concurrent.ListenableFutureCallbackRegistry;
import org.springframework.util.concurrent.SuccessCallback;
import reactor.fn.Consumer;
import reactor.rx.Promise;
/**
* Adapts a reactor {@link Promise} to {@link ListenableFuture} optionally converting
@@ -55,21 +56,20 @@ abstract class AbstractPromiseToListenableFutureAdapter<S, T> implements Listena
try {
registry.success(adapt(result));
}
catch (Throwable t) {
registry.failure(t);
catch (Throwable ex) {
registry.failure(ex);
}
}
});
this.promise.onError(new Consumer<Throwable>() {
@Override
public void accept(Throwable t) {
registry.failure(t);
public void accept(Throwable ex) {
registry.failure(ex);
}
});
}
protected abstract T adapt(S result);
@Override
public T get() throws InterruptedException {
@@ -112,4 +112,7 @@ abstract class AbstractPromiseToListenableFutureAdapter<S, T> implements Listena
this.registry.addFailureCallback(failureCallback);
}
protected abstract T adapt(S result);
}

View File

@@ -60,7 +60,6 @@ import org.springframework.messaging.tcp.ReconnectStrategy;
import org.springframework.messaging.tcp.TcpConnectionHandler;
import org.springframework.messaging.tcp.TcpOperations;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.concurrent.ListenableFuture;
@@ -83,7 +82,6 @@ public class Reactor2TcpClient<P> implements TcpOperations<P> {
private static final Method eventLoopGroupMethod = initEventLoopGroupMethod();
private final EventLoopGroup eventLoopGroup;
private final TcpClientFactory<Message<P>, Message<P>> tcpClientSpecFactory;
@@ -107,7 +105,6 @@ public class Reactor2TcpClient<P> implements TcpOperations<P> {
* @param codec the codec to use for encoding and decoding the TCP stream
*/
public Reactor2TcpClient(final String host, final int port, final Codec<Buffer, Message<P>, Message<P>> codec) {
// Reactor 2.0.5 requires NioEventLoopGroup vs 2.0.6+ requires EventLoopGroup
final NioEventLoopGroup nioEventLoopGroup = initEventLoopGroup();
this.eventLoopGroup = nioEventLoopGroup;
@@ -182,8 +179,8 @@ public class Reactor2TcpClient<P> implements TcpOperations<P> {
return new PassThroughPromiseToListenableFutureAdapter<Void>(
promise.onError(new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) {
connectionHandler.afterConnectFailure(throwable);
public void accept(Throwable ex) {
connectionHandler.afterConnectFailure(ex);
}
})
);
@@ -262,7 +259,7 @@ public class Reactor2TcpClient<P> implements TcpOperations<P> {
return method;
}
}
throw new IllegalStateException("No compatible Reactor version found.");
throw new IllegalStateException("No compatible Reactor version found");
}
@@ -270,8 +267,8 @@ public class Reactor2TcpClient<P> implements TcpOperations<P> {
@Override
public ReactorConfiguration read() {
return new ReactorConfiguration(Collections.<DispatcherConfiguration>emptyList(),
"sync", new Properties());
return new ReactorConfiguration(
Collections.<DispatcherConfiguration>emptyList(), "sync", new Properties());
}
}

View File

@@ -38,7 +38,6 @@ import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator;
/**
* Adapts {@link WebSocketHandler} to the Jetty 9 WebSocket API.
*
@@ -59,8 +58,8 @@ public class JettyWebSocketHandlerAdapter {
public JettyWebSocketHandlerAdapter(WebSocketHandler webSocketHandler, JettyWebSocketSession wsSession) {
Assert.notNull(webSocketHandler, "webSocketHandler must not be null");
Assert.notNull(wsSession, "wsSession must not be null");
Assert.notNull(webSocketHandler, "WebSocketHandler must not be null");
Assert.notNull(wsSession, "WebSocketSession must not be null");
this.webSocketHandler = webSocketHandler;
this.wsSession = wsSession;
}
@@ -72,8 +71,8 @@ public class JettyWebSocketHandlerAdapter {
this.wsSession.initializeNativeSession(session);
this.webSocketHandler.afterConnectionEstablished(this.wsSession);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}
@@ -83,8 +82,8 @@ public class JettyWebSocketHandlerAdapter {
try {
this.webSocketHandler.handleMessage(this.wsSession, message);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}
@@ -94,8 +93,8 @@ public class JettyWebSocketHandlerAdapter {
try {
this.webSocketHandler.handleMessage(this.wsSession, message);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}
@@ -107,8 +106,8 @@ public class JettyWebSocketHandlerAdapter {
try {
this.webSocketHandler.handleMessage(this.wsSession, message);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}
}
@@ -119,8 +118,10 @@ public class JettyWebSocketHandlerAdapter {
try {
this.webSocketHandler.afterConnectionClosed(this.wsSession, closeStatus);
}
catch (Throwable t) {
logger.error("Unhandled error for " + this.wsSession, t);
catch (Throwable ex) {
if (logger.isErrorEnabled()) {
logger.error("Unhandled error for " + this.wsSession, ex);
}
}
}
@@ -129,8 +130,8 @@ public class JettyWebSocketHandlerAdapter {
try {
this.webSocketHandler.handleTransportError(this.wsSession, cause);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* 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.
@@ -49,8 +49,8 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
public StandardWebSocketHandlerAdapter(WebSocketHandler handler, StandardWebSocketSession wsSession) {
Assert.notNull(handler, "handler must not be null");
Assert.notNull(wsSession, "wsSession must not be null");
Assert.notNull(handler, "WebSocketHandler must not be null");
Assert.notNull(wsSession, "WebSocketSession must not be null");
this.handler = handler;
this.wsSession = wsSession;
}
@@ -58,7 +58,6 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
@Override
public void onOpen(final javax.websocket.Session session, EndpointConfig config) {
this.wsSession.initializeNativeSession(session);
if (this.handler.supportsPartialMessages()) {
@@ -100,9 +99,8 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
try {
this.handler.afterConnectionEstablished(this.wsSession);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
return;
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}
@@ -111,8 +109,8 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
try {
this.handler.handleMessage(this.wsSession, textMessage);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}
@@ -121,8 +119,8 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
try {
this.handler.handleMessage(this.wsSession, binaryMessage);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}
@@ -131,8 +129,8 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
try {
this.handler.handleMessage(this.wsSession, pongMessage);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}
@@ -142,8 +140,10 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
try {
this.handler.afterConnectionClosed(this.wsSession, closeStatus);
}
catch (Throwable t) {
logger.error("Unhandled error for " + this.wsSession, t);
catch (Throwable ex) {
if (logger.isErrorEnabled()) {
logger.error("Unhandled error for " + this.wsSession, ex);
}
}
}
@@ -152,8 +152,8 @@ public class StandardWebSocketHandlerAdapter extends Endpoint {
try {
this.handler.handleTransportError(this.wsSession, exception);
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, t, logger);
catch (Throwable ex) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* 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.
@@ -25,16 +25,16 @@ import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
/**
* An exception handling {@link WebSocketHandlerDecorator}. Traps all {@link Throwable}
* instances that escape from the decorated handler and closes the session with
* {@link CloseStatus#SERVER_ERROR}.
* An exception handling {@link WebSocketHandlerDecorator}.
* Traps all {@link Throwable} instances that escape from the decorated
* handler and closes the session with {@link CloseStatus#SERVER_ERROR}.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class ExceptionWebSocketHandlerDecorator extends WebSocketHandlerDecorator {
private final Log logger = LogFactory.getLog(ExceptionWebSocketHandlerDecorator.class);
private static final Log logger = LogFactory.getLog(ExceptionWebSocketHandlerDecorator.class);
public ExceptionWebSocketHandlerDecorator(WebSocketHandler delegate) {
@@ -52,20 +52,6 @@ public class ExceptionWebSocketHandlerDecorator extends WebSocketHandlerDecorato
}
}
public static void tryCloseWithError(WebSocketSession session, Throwable exception, Log logger) {
if (logger.isDebugEnabled()) {
logger.debug("Closing due to exception for " + session, exception);
}
if (session.isOpen()) {
try {
session.close(CloseStatus.SERVER_ERROR);
}
catch (Throwable t) {
// ignore
}
}
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
try {
@@ -91,8 +77,25 @@ public class ExceptionWebSocketHandlerDecorator extends WebSocketHandlerDecorato
try {
getDelegate().afterConnectionClosed(session, closeStatus);
}
catch (Throwable t) {
logger.error("Unhandled error for " + this, t);
catch (Throwable ex) {
if (logger.isErrorEnabled()) {
logger.error("Unhandled error for " + this, ex);
}
}
}
public static void tryCloseWithError(WebSocketSession session, Throwable exception, Log logger) {
if (logger.isDebugEnabled()) {
logger.debug("Closing due to exception for " + session, exception);
}
if (session.isOpen()) {
try {
session.close(CloseStatus.SERVER_ERROR);
}
catch (Throwable ex) {
// ignore
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* 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.
@@ -22,10 +22,8 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
@@ -69,11 +67,13 @@ public class PerConnectionWebSocketHandler implements WebSocketHandler, BeanFact
this.supportsPartialMessages = supportsPartialMessages;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
public void setBeanFactory(BeanFactory beanFactory) {
this.provider.setBeanFactory(beanFactory);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
WebSocketHandler handler = this.provider.getHandler();
@@ -86,12 +86,6 @@ public class PerConnectionWebSocketHandler implements WebSocketHandler, BeanFact
getHandler(session).handleMessage(session, message);
}
private WebSocketHandler getHandler(WebSocketSession session) {
WebSocketHandler handler = this.handlers.get(session);
Assert.isTrue(handler != null, "WebSocketHandler not found for " + session);
return handler;
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
getHandler(session).handleTransportError(session, exception);
@@ -103,19 +97,7 @@ public class PerConnectionWebSocketHandler implements WebSocketHandler, BeanFact
getHandler(session).afterConnectionClosed(session, closeStatus);
}
finally {
destroy(session);
}
}
private void destroy(WebSocketSession session) {
WebSocketHandler handler = this.handlers.remove(session);
try {
if (handler != null) {
this.provider.destroy(handler);
}
}
catch (Throwable t) {
logger.warn("Error while destroying " + handler, t);
destroyHandler(session);
}
}
@@ -124,6 +106,30 @@ public class PerConnectionWebSocketHandler implements WebSocketHandler, BeanFact
return this.supportsPartialMessages;
}
private WebSocketHandler getHandler(WebSocketSession session) {
WebSocketHandler handler = this.handlers.get(session);
if (handler == null) {
throw new IllegalStateException("WebSocketHandler not found for " + session);
}
return handler;
}
private void destroyHandler(WebSocketSession session) {
WebSocketHandler handler = this.handlers.remove(session);
try {
if (handler != null) {
this.provider.destroy(handler);
}
}
catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Error while destroying " + handler, ex);
}
}
}
@Override
public String toString() {
return "PerConnectionWebSocketHandlerProxy[handlerType=" + this.provider.getHandlerType() + "]";

View File

@@ -18,8 +18,8 @@ package org.springframework.web.socket.messaging;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -86,7 +86,7 @@ public class SubProtocolWebSocketHandler
private final Map<String, SubProtocolHandler> protocolHandlerLookup =
new TreeMap<String, SubProtocolHandler>(String.CASE_INSENSITIVE_ORDER);
private final Set<SubProtocolHandler> protocolHandlers = new HashSet<SubProtocolHandler>();
private final Set<SubProtocolHandler> protocolHandlers = new LinkedHashSet<SubProtocolHandler>();
private SubProtocolHandler defaultProtocolHandler;
@@ -171,7 +171,7 @@ public class SubProtocolWebSocketHandler
public void setDefaultProtocolHandler(SubProtocolHandler defaultProtocolHandler) {
this.defaultProtocolHandler = defaultProtocolHandler;
if (this.protocolHandlerLookup.isEmpty()) {
setProtocolHandlers(Arrays.asList(defaultProtocolHandler));
setProtocolHandlers(Collections.singletonList(defaultProtocolHandler));
}
}
@@ -262,8 +262,10 @@ public class SubProtocolWebSocketHandler
try {
holder.getSession().close(CloseStatus.GOING_AWAY);
}
catch (Throwable t) {
logger.error("Failed to close '" + holder.getSession() + "': " + t.getMessage());
catch (Throwable ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to close '" + holder.getSession() + "': " + ex);
}
}
}
}
@@ -290,36 +292,6 @@ public class SubProtocolWebSocketHandler
findProtocolHandler(session).afterSessionStarted(session, this.clientInboundChannel);
}
protected final SubProtocolHandler findProtocolHandler(WebSocketSession session) {
String protocol = null;
try {
protocol = session.getAcceptedProtocol();
}
catch (Exception ex) {
// Shouldn't happen
logger.error("Failed to obtain session.getAcceptedProtocol(). Will use the " +
"default protocol handler (if configured).", ex);
}
SubProtocolHandler handler;
if (!StringUtils.isEmpty(protocol)) {
handler = this.protocolHandlerLookup.get(protocol);
Assert.state(handler != null, "No handler for '" + protocol + "' among " + this.protocolHandlerLookup);
}
else {
if (this.defaultProtocolHandler != null) {
handler = this.defaultProtocolHandler;
}
else if (this.protocolHandlers.size() == 1) {
handler = this.protocolHandlers.iterator().next();
}
else {
throw new IllegalStateException("Multiple protocol handlers configured and " +
"no protocol was negotiated. Consider configuring a default SubProtocolHandler.");
}
}
return handler;
}
/**
* Handle an inbound message from a WebSocket client.
*/
@@ -378,6 +350,56 @@ public class SubProtocolWebSocketHandler
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
this.stats.incrementTransportError();
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
clearSession(session, closeStatus);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
protected final SubProtocolHandler findProtocolHandler(WebSocketSession session) {
String protocol = null;
try {
protocol = session.getAcceptedProtocol();
}
catch (Exception ex) {
// Shouldn't happen
logger.error("Failed to obtain session.getAcceptedProtocol(). " +
"Will use the default protocol handler (if configured).", ex);
}
SubProtocolHandler handler;
if (!StringUtils.isEmpty(protocol)) {
handler = this.protocolHandlerLookup.get(protocol);
if (handler == null) {
throw new IllegalStateException(
"No handler for '" + protocol + "' among " + this.protocolHandlerLookup);
}
}
else {
if (this.defaultProtocolHandler != null) {
handler = this.defaultProtocolHandler;
}
else if (this.protocolHandlers.size() == 1) {
handler = this.protocolHandlers.iterator().next();
}
else {
throw new IllegalStateException("Multiple protocol handlers configured and " +
"no protocol was negotiated. Consider configuring a default SubProtocolHandler.");
}
}
return handler;
}
private String resolveSessionId(Message<?> message) {
for (SubProtocolHandler handler : this.protocolHandlerLookup.values()) {
String sessionId = handler.resolveSessionId(message);
@@ -408,6 +430,7 @@ public class SubProtocolWebSocketHandler
if (!isRunning() || (currentTime - this.lastSessionCheckTime < TIME_TO_FIRST_MESSAGE)) {
return;
}
if (this.sessionCheckLock.tryLock()) {
try {
for (WebSocketSessionHolder holder : this.sessions.values()) {
@@ -427,8 +450,10 @@ public class SubProtocolWebSocketHandler
this.stats.incrementNoMessagesReceivedCount();
session.close(CloseStatus.SESSION_NOT_RELIABLE);
}
catch (Throwable t) {
logger.error("Failure while closing " + session, t);
catch (Throwable ex) {
if (logger.isErrorEnabled()) {
logger.error("Failure while closing " + session, ex);
}
}
}
}
@@ -439,16 +464,6 @@ public class SubProtocolWebSocketHandler
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
this.stats.incrementTransportError();
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
clearSession(session, closeStatus);
}
private void clearSession(WebSocketSession session, CloseStatus closeStatus) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Clearing session " + session.getId());
@@ -459,16 +474,13 @@ public class SubProtocolWebSocketHandler
findProtocolHandler(session).afterSessionEnded(session, closeStatus, this.clientInboundChannel);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
@Override
public String toString() {
return "SubProtocolWebSocketHandler" + getProtocolHandlers();
return "SubProtocolWebSocketHandler" + this.protocolHandlers;
}
private static class WebSocketSessionHolder {
private final WebSocketSession session;
@@ -477,7 +489,6 @@ public class SubProtocolWebSocketHandler
private volatile boolean handledMessages;
private WebSocketSessionHolder(WebSocketSession session) {
this.session = session;
}
@@ -500,11 +511,12 @@ public class SubProtocolWebSocketHandler
@Override
public String toString() {
return "WebSocketSessionHolder[=session=" + this.session + ", createTime=" +
return "WebSocketSessionHolder[session=" + this.session + ", createTime=" +
this.createTime + ", hasHandledMessages=" + this.handledMessages + "]";
}
}
private class Stats {
private final AtomicInteger total = new AtomicInteger();
@@ -521,7 +533,6 @@ public class SubProtocolWebSocketHandler
private final AtomicInteger transportError = new AtomicInteger();
public void incrementSessionCount(WebSocketSession session) {
getCountFor(session).incrementAndGet();
this.total.incrementAndGet();