Browse Source

Polishing

pull/642/head
Juergen Hoeller 11 years ago
parent
commit
97bd0ccfec
  1. 6
      spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java
  2. 14
      spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureAdapter.java
  3. 6
      spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallback.java
  4. 46
      spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java
  5. 18
      spring-core/src/test/java/org/springframework/util/concurrent/ListenableFutureTaskTests.java
  6. 128
      spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java
  7. 197
      spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java
  8. 81
      spring-web/src/test/java/org/springframework/http/client/AbstractAsyncHttpRequestFactoryTestCase.java
  9. 225
      spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java
  10. 17
      spring-websocket/src/main/java/org/springframework/web/socket/client/WebSocketConnectionManager.java

6
spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -49,8 +49,7 @@ import org.springframework.util.Assert;
* @see #setBeforeExistingAdvisors * @see #setBeforeExistingAdvisors
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {
implements BeanFactoryAware {
private Class<? extends Annotation> asyncAnnotationType; private Class<? extends Annotation> asyncAnnotationType;
@ -61,6 +60,7 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostPr
setBeforeExistingAdvisors(true); setBeforeExistingAdvisors(true);
} }
/** /**
* Set the 'async' annotation type to be detected at either class or method * Set the 'async' annotation type to be detected at either class or method
* level. By default, both the {@link Async} annotation and the EJB 3.1 * level. By default, both the {@link Async} annotation and the EJB 3.1

14
spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureAdapter.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,18 +30,16 @@ import java.util.concurrent.ExecutionException;
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 4.0 * @since 4.0
*/ */
public abstract class ListenableFutureAdapter<T, S> extends FutureAdapter<T, S> public abstract class ListenableFutureAdapter<T, S> extends FutureAdapter<T, S> implements ListenableFuture<T> {
implements ListenableFuture<T> {
/** /**
* Constructs a new {@code ListenableFutureAdapter} with the given adaptee. * Construct a new {@code ListenableFutureAdapter} with the given adaptee.
* @param adaptee the future to adaptee to * @param adaptee the future to adaptee to
*/ */
protected ListenableFutureAdapter(ListenableFuture<S> adaptee) { protected ListenableFutureAdapter(ListenableFuture<S> adaptee) {
super(adaptee); super(adaptee);
} }
@Override @Override
public void addCallback(final ListenableFutureCallback<? super T> callback) { public void addCallback(final ListenableFutureCallback<? super T> callback) {
ListenableFuture<S> listenableAdaptee = (ListenableFuture<S>) getAdaptee(); ListenableFuture<S> listenableAdaptee = (ListenableFuture<S>) getAdaptee();
@ -59,11 +57,11 @@ public abstract class ListenableFutureAdapter<T, S> extends FutureAdapter<T, S>
onFailure(t); onFailure(t);
} }
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
callback.onFailure(t); callback.onFailure(ex);
} }
}); });
} }
} }

6
spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallback.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -33,8 +33,8 @@ public interface ListenableFutureCallback<T> {
/** /**
* Called when the {@link ListenableFuture} fails to complete. * Called when the {@link ListenableFuture} fails to complete.
* @param t the exception that triggered the failure * @param ex the exception that triggered the failure
*/ */
void onFailure(Throwable t); void onFailure(Throwable ex);
} }

46
spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -48,54 +48,52 @@ public class ListenableFutureCallbackRegistry<T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void addCallback(ListenableFutureCallback<? super T> callback) { public void addCallback(ListenableFutureCallback<? super T> callback) {
Assert.notNull(callback, "'callback' must not be null"); Assert.notNull(callback, "'callback' must not be null");
synchronized (this.mutex) {
synchronized (mutex) { switch (this.state) {
switch (state) {
case NEW: case NEW:
callbacks.add(callback); this.callbacks.add(callback);
break; break;
case SUCCESS: case SUCCESS:
callback.onSuccess((T)result); callback.onSuccess((T) this.result);
break; break;
case FAILURE: case FAILURE:
callback.onFailure((Throwable) result); callback.onFailure((Throwable) this.result);
break; break;
} }
} }
} }
/** /**
* Triggers a {@link ListenableFutureCallback#onSuccess(Object)} call on all added * Triggers a {@link ListenableFutureCallback#onSuccess(Object)} call on all
* callbacks with the given result * added callbacks with the given result.
* @param result the result to trigger the callbacks with * @param result the result to trigger the callbacks with
*/ */
public void success(T result) { public void success(T result) {
synchronized (mutex) { synchronized (this.mutex) {
state = State.SUCCESS; this.state = State.SUCCESS;
this.result = result; this.result = result;
while (!this.callbacks.isEmpty()) {
while (!callbacks.isEmpty()) { this.callbacks.poll().onSuccess(result);
callbacks.poll().onSuccess(result);
} }
} }
} }
/** /**
* Triggers a {@link ListenableFutureCallback#onFailure(Throwable)} call on all added * Triggers a {@link ListenableFutureCallback#onFailure(Throwable)} call on all
* callbacks with the given {@code Throwable}. * added callbacks with the given {@code Throwable}.
* @param t the exception to trigger the callbacks with * @param ex the exception to trigger the callbacks with
*/ */
public void failure(Throwable t) { public void failure(Throwable ex) {
synchronized (mutex) { synchronized (this.mutex) {
state = State.FAILURE; this.state = State.FAILURE;
this.result = t; this.result = ex;
while (!this.callbacks.isEmpty()) {
while (!callbacks.isEmpty()) { this.callbacks.poll().onFailure(ex);
callbacks.poll().onFailure(t);
} }
} }
} }
private enum State {NEW, SUCCESS, FAILURE} private enum State {NEW, SUCCESS, FAILURE}
} }

18
spring-core/src/test/java/org/springframework/util/concurrent/ListenableFutureTaskTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,10 +20,10 @@ import java.io.IOException;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
*/ */
@ -44,10 +44,9 @@ public class ListenableFutureTaskTests {
public void onSuccess(String result) { public void onSuccess(String result) {
assertEquals(s, result); assertEquals(s, result);
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
task.run(); task.run();
@ -68,15 +67,12 @@ public class ListenableFutureTaskTests {
public void onSuccess(String result) { public void onSuccess(String result) {
fail("onSuccess not expected"); fail("onSuccess not expected");
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
assertEquals(s, t.getMessage()); assertEquals(s, ex.getMessage());
} }
}); });
task.run(); task.run();
} }
} }

128
spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java

@ -41,8 +41,10 @@ import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.util.concurrent.ListenableFutureTask; import org.springframework.util.concurrent.ListenableFutureTask;
/** /**
* A {@link org.springframework.messaging.MessageHandler} that handles messages by forwarding them to a STOMP broker. * A {@link org.springframework.messaging.MessageHandler} that handles messages by
* For each new {@link SimpMessageType#CONNECT CONNECT} message, an independent TCP * forwarding them to a STOMP broker.
*
* <p>For each new {@link SimpMessageType#CONNECT CONNECT} message, an independent TCP
* connection to the broker is opened and used exclusively for all messages from the * connection to the broker is opened and used exclusively for all messages from the
* client that originated the CONNECT message. Messages from the same client are * client that originated the CONNECT message. Messages from the same client are
* identified through the session id message header. Reversely, when the STOMP broker * identified through the session id message header. Reversely, when the STOMP broker
@ -57,10 +59,10 @@ import org.springframework.util.concurrent.ListenableFutureTask;
* shared and cannot be used to receive messages. Several properties are provided to * shared and cannot be used to receive messages. Several properties are provided to
* configure the "system" connection including: * configure the "system" connection including:
* <ul> * <ul>
* <li>{@link #setSystemLogin(String)}</li> * <li>{@link #setSystemLogin(String)}</li>
* <li>{@link #setSystemPasscode(String)}</li> * <li>{@link #setSystemPasscode(String)}</li>
* <li>{@link #setSystemHeartbeatSendInterval(long)}</li> * <li>{@link #setSystemHeartbeatSendInterval(long)}</li>
* <li>{@link #setSystemHeartbeatReceiveInterval(long)}</li> * <li>{@link #setSystemHeartbeatReceiveInterval(long)}</li>
* </ul> * </ul>
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -84,10 +86,11 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
private static final Message<byte[]> HEARTBEAT_MESSAGE; private static final Message<byte[]> HEARTBEAT_MESSAGE;
static { static {
EMPTY_TASK.run();
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.HEARTBEAT); SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.HEARTBEAT);
HEARTBEAT_MESSAGE = MessageBuilder.withPayload(new byte[] {'\n'}).setHeaders(headers).build(); HEARTBEAT_MESSAGE = MessageBuilder.withPayload(new byte[] {'\n'}).setHeaders(headers).build();
EMPTY_TASK.run();
} }
@ -124,7 +127,6 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
/** /**
* Create a StompBrokerRelayMessageHandler instance with the given message channels * Create a StompBrokerRelayMessageHandler instance with the given message channels
* and destination prefixes. * and destination prefixes.
*
* @param clientInChannel the channel for receiving messages from clients (e.g. WebSocket clients) * @param clientInChannel the channel for receiving messages from clients (e.g. WebSocket clients)
* @param clientOutChannel the channel for sending messages to clients (e.g. WebSocket clients) * @param clientOutChannel the channel for sending messages to clients (e.g. WebSocket clients)
* @param brokerChannel the channel for the application to send messages to the broker * @param brokerChannel the channel for the application to send messages to the broker
@ -135,11 +137,9 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
SubscribableChannel brokerChannel, Collection<String> destinationPrefixes) { SubscribableChannel brokerChannel, Collection<String> destinationPrefixes) {
super(destinationPrefixes); super(destinationPrefixes);
Assert.notNull(clientInChannel, "'clientInChannel' must not be null"); Assert.notNull(clientInChannel, "'clientInChannel' must not be null");
Assert.notNull(clientOutChannel, "'clientOutChannel' must not be null"); Assert.notNull(clientOutChannel, "'clientOutChannel' must not be null");
Assert.notNull(brokerChannel, "'brokerChannel' must not be null"); Assert.notNull(brokerChannel, "'brokerChannel' must not be null");
this.clientInboundChannel = clientInChannel; this.clientInboundChannel = clientInChannel;
this.clientOutboundChannel = clientOutChannel; this.clientOutboundChannel = clientOutChannel;
this.brokerChannel = brokerChannel; this.brokerChannel = brokerChannel;
@ -155,7 +155,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* @return the STOMP message broker host. * Return the STOMP message broker host.
*/ */
public String getRelayHost() { public String getRelayHost() {
return this.relayHost; return this.relayHost;
@ -169,7 +169,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* @return the STOMP message broker port. * Return the STOMP message broker port.
*/ */
public int getRelayPort() { public int getRelayPort() {
return this.relayPort; return this.relayPort;
@ -187,7 +187,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* @return The interval, in milliseconds, at which the "system" connection will * Return the interval, in milliseconds, at which the "system" connection will
* send heartbeats to the STOMP broker. * send heartbeats to the STOMP broker.
*/ */
public long getSystemHeartbeatSendInterval() { public long getSystemHeartbeatSendInterval() {
@ -207,7 +207,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* @return The interval, in milliseconds, at which the "system" connection expects * Return the interval, in milliseconds, at which the "system" connection expects
* to receive heartbeats from the STOMP broker. * to receive heartbeats from the STOMP broker.
*/ */
public long getSystemHeartbeatReceiveInterval() { public long getSystemHeartbeatReceiveInterval() {
@ -217,8 +217,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
/** /**
* Set the login to use when creating connections to the STOMP broker on * Set the login to use when creating connections to the STOMP broker on
* behalf of connected clients. * behalf of connected clients.
* <p> * <p>By default this is set to "guest".
* By default this is set to "guest".
* @see #setSystemLogin(String) * @see #setSystemLogin(String)
*/ */
public void setClientLogin(String clientLogin) { public void setClientLogin(String clientLogin) {
@ -227,7 +226,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* @return the configured login to use for connections to the STOMP broker * Return the configured login to use for connections to the STOMP broker
* on behalf of connected clients. * on behalf of connected clients.
* @see #getSystemLogin() * @see #getSystemLogin()
*/ */
@ -236,11 +235,10 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* Set the clientPasscode to use to create connections to the STOMP broker on * Set the client passcode to use to create connections to the STOMP broker on
* behalf of connected clients. * behalf of connected clients.
* <p> * <p>By default this is set to "guest".
* By default this is set to "guest". * @see #setSystemPasscode
* @see #setSystemPasscode(String)
*/ */
public void setClientPasscode(String clientPasscode) { public void setClientPasscode(String clientPasscode) {
Assert.hasText(clientPasscode, "clientPasscode must not be empty"); Assert.hasText(clientPasscode, "clientPasscode must not be empty");
@ -248,7 +246,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* @return the configured passocde to use for connections to the STOMP broker on * Return the configured passcode to use for connections to the STOMP broker on
* behalf of connected clients. * behalf of connected clients.
* @see #getSystemPasscode() * @see #getSystemPasscode()
*/ */
@ -260,8 +258,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
* Set the login for the shared "system" connection used to send messages to * Set the login for the shared "system" connection used to send messages to
* the STOMP broker from within the application, i.e. messages not associated * the STOMP broker from within the application, i.e. messages not associated
* with a specific client session (e.g. REST/HTTP request handling method). * with a specific client session (e.g. REST/HTTP request handling method).
* <p> * <p>By default this is set to "guest".
* By default this is set to "guest".
*/ */
public void setSystemLogin(String systemLogin) { public void setSystemLogin(String systemLogin) {
Assert.hasText(systemLogin, "systemLogin must not be empty"); Assert.hasText(systemLogin, "systemLogin must not be empty");
@ -269,7 +266,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* @return the login used for the shared "system" connection to the STOMP broker * Return the login used for the shared "system" connection to the STOMP broker.
*/ */
public String getSystemLogin() { public String getSystemLogin() {
return this.systemLogin; return this.systemLogin;
@ -279,15 +276,14 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
* Set the passcode for the shared "system" connection used to send messages to * Set the passcode for the shared "system" connection used to send messages to
* the STOMP broker from within the application, i.e. messages not associated * the STOMP broker from within the application, i.e. messages not associated
* with a specific client session (e.g. REST/HTTP request handling method). * with a specific client session (e.g. REST/HTTP request handling method).
* <p> * <p>By default this is set to "guest".
* By default this is set to "guest".
*/ */
public void setSystemPasscode(String systemPasscode) { public void setSystemPasscode(String systemPasscode) {
this.systemPasscode = systemPasscode; this.systemPasscode = systemPasscode;
} }
/** /**
* @return the passcode used for the shared "system" connection to the STOMP broker * Return the passcode used for the shared "system" connection to the STOMP broker.
*/ */
public String getSystemPasscode() { public String getSystemPasscode() {
return this.systemPasscode; return this.systemPasscode;
@ -306,7 +302,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
/** /**
* @return the configured virtual host value. * Return the configured virtual host value.
*/ */
public String getVirtualHost() { public String getVirtualHost() {
return this.virtualHost; return this.virtualHost;
@ -367,7 +363,6 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
@Override @Override
protected void stopInternal() { protected void stopInternal() {
publishBrokerUnavailableEvent(); publishBrokerUnavailableEvent();
this.clientInboundChannel.unsubscribe(this); this.clientInboundChannel.unsubscribe(this);
@ -376,19 +371,18 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
try { try {
this.tcpClient.shutdown().get(5000, TimeUnit.MILLISECONDS); this.tcpClient.shutdown().get(5000, TimeUnit.MILLISECONDS);
} }
catch (Throwable t) { catch (Throwable ex) {
logger.error("Error while shutting down TCP client", t); logger.error("Error in shutdown of TCP client", ex);
} }
} }
@Override @Override
protected void handleMessageInternal(Message<?> message) { protected void handleMessageInternal(Message<?> message) {
StompHeaderAccessor headers = StompHeaderAccessor.wrap(message); StompHeaderAccessor headers = StompHeaderAccessor.wrap(message);
String sessionId = headers.getSessionId(); String sessionId = headers.getSessionId();
if (!isBrokerAvailable()) { if (!isBrokerAvailable()) {
if (sessionId == null || sessionId == SystemStompConnectionHandler.SESSION_ID) { if (sessionId == null || sessionId.equals(SystemStompConnectionHandler.SESSION_ID)) {
throw new MessageDeliveryException("Message broker is not active."); throw new MessageDeliveryException("Message broker is not active.");
} }
if (SimpMessageType.CONNECT.equals(headers.getMessageType()) && logger.isErrorEnabled()) { if (SimpMessageType.CONNECT.equals(headers.getMessageType()) && logger.isErrorEnabled()) {
@ -418,7 +412,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
return; return;
} }
if ((command != null) && command.requiresDestination() && !checkDestinationPrefix(destination)) { if (command != null && command.requiresDestination() && !checkDestinationPrefix(destination)) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Ignoring message to destination=" + destination); logger.trace("Ignoring message to destination=" + destination);
} }
@ -482,15 +476,12 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
this(sessionId, connectHeaders, true); this(sessionId, connectHeaders, true);
} }
private StompConnectionHandler(String sessionId, StompHeaderAccessor connectHeaders, private StompConnectionHandler(String sessionId, StompHeaderAccessor connectHeaders, boolean isClientSession) {
boolean isRemoteClientSession) { Assert.notNull(sessionId, "'sessionId' must not be null");
Assert.notNull(connectHeaders, "'connectHeaders' must not be null");
Assert.notNull(sessionId, "SessionId must not be null");
Assert.notNull(connectHeaders, "ConnectHeaders must not be null");
this.sessionId = sessionId; this.sessionId = sessionId;
this.connectHeaders = connectHeaders; this.connectHeaders = connectHeaders;
this.isRemoteClientSession = isRemoteClientSession; this.isRemoteClientSession = isClientSession;
} }
public String getSessionId() { public String getSessionId() {
@ -513,7 +504,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
/** /**
* Invoked when any TCP connectivity issue is detected, i.e. failure to establish * Invoked when any TCP connectivity issue is detected, i.e. failure to establish
* the TCP connection, failure to send a message, missed heartbeat. * the TCP connection, failure to send a message, missed heartbeat, etc.
*/ */
protected void handleTcpConnectionFailure(String errorMessage, Throwable ex) { protected void handleTcpConnectionFailure(String errorMessage, Throwable ex) {
if (logger.isErrorEnabled()) { if (logger.isErrorEnabled()) {
@ -526,9 +517,9 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
try { try {
clearConnection(); clearConnection();
} }
catch (Throwable t) { catch (Throwable ex2) {
if (logger.isErrorEnabled()) { if (logger.isErrorEnabled()) {
logger.error("Failed to close connection: " + t.getMessage()); logger.error("Failed to close connection: " + ex2.getMessage());
} }
} }
} }
@ -552,7 +543,6 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
@Override @Override
public void handleMessage(Message<byte[]> message) { public void handleMessage(Message<byte[]> message) {
StompHeaderAccessor headers = StompHeaderAccessor.wrap(message); StompHeaderAccessor headers = StompHeaderAccessor.wrap(message);
headers.setSessionId(this.sessionId); headers.setSessionId(this.sessionId);
@ -584,7 +574,6 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
private void initHeartbeats(StompHeaderAccessor connectedHeaders) { private void initHeartbeats(StompHeaderAccessor connectedHeaders) {
// Remote clients do their own heartbeat management // Remote clients do their own heartbeat management
if (this.isRemoteClientSession) { if (this.isRemoteClientSession) {
return; return;
@ -592,11 +581,10 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
long clientSendInterval = this.connectHeaders.getHeartbeat()[0]; long clientSendInterval = this.connectHeaders.getHeartbeat()[0];
long clientReceiveInterval = this.connectHeaders.getHeartbeat()[1]; long clientReceiveInterval = this.connectHeaders.getHeartbeat()[1];
long serverSendInterval = connectedHeaders.getHeartbeat()[0]; long serverSendInterval = connectedHeaders.getHeartbeat()[0];
long serverReceiveInterval = connectedHeaders.getHeartbeat()[1]; long serverReceiveInterval = connectedHeaders.getHeartbeat()[1];
if ((clientSendInterval > 0) && (serverReceiveInterval > 0)) { if (clientSendInterval > 0 && serverReceiveInterval > 0) {
long interval = Math.max(clientSendInterval, serverReceiveInterval); long interval = Math.max(clientSendInterval, serverReceiveInterval);
this.tcpConnection.onWriteInactivity(new Runnable() { this.tcpConnection.onWriteInactivity(new Runnable() {
@Override @Override
@ -605,10 +593,11 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
if (conn != null) { if (conn != null) {
conn.send(HEARTBEAT_MESSAGE).addCallback( conn.send(HEARTBEAT_MESSAGE).addCallback(
new ListenableFutureCallback<Void>() { new ListenableFutureCallback<Void>() {
public void onFailure(Throwable t) { public void onSuccess(Void result) {
handleTcpConnectionFailure("Failed to send heartbeat", t); }
public void onFailure(Throwable ex) {
handleTcpConnectionFailure("Failed to send heartbeat", ex);
} }
public void onSuccess(Void result) {}
}); });
} }
} }
@ -620,8 +609,8 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
this.tcpConnection.onReadInactivity(new Runnable() { this.tcpConnection.onReadInactivity(new Runnable() {
@Override @Override
public void run() { public void run() {
handleTcpConnectionFailure("No hearbeat from broker for more than " + handleTcpConnectionFailure("No heartbeat from broker for more than " + interval +
interval + "ms, closing connection", null); "ms, closing connection", null);
} }
}, interval); }, interval);
} }
@ -652,17 +641,14 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
this.tcpConnection = null; this.tcpConnection = null;
clearConnection(); clearConnection();
} }
catch (Throwable t) { catch (Throwable ex) {
if (logger.isErrorEnabled()) { // Shouldn't happen with connection reset beforehand
// Ignore
}
} }
} }
} }
/** /**
* Forward the given message to the STOMP broker. * Forward the given message to the STOMP broker.
*
* <p>The method checks whether we have an active TCP connection and have * <p>The method checks whether we have an active TCP connection and have
* received the STOMP CONNECTED frame. For client messages this should be * received the STOMP CONNECTED frame. For client messages this should be
* false only if we lose the TCP connection around the same time when a * false only if we lose the TCP connection around the same time when a
@ -671,7 +657,6 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
* the "system" connection an exception is raised so that components sending * the "system" connection an exception is raised so that components sending
* the message have a chance to handle it -- by default the broker message * the message have a chance to handle it -- by default the broker message
* channel is synchronous. * channel is synchronous.
*
* <p>Note that if messages arrive concurrently around the same time a TCP * <p>Note that if messages arrive concurrently around the same time a TCP
* connection is lost, there is a brief period of time before the connection * connection is lost, there is a brief period of time before the connection
* is reset when one or more messages may sneak through and an attempt made * is reset when one or more messages may sneak through and an attempt made
@ -679,13 +664,11 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
* method simply lets them try and fail. For client sessions that may * method simply lets them try and fail. For client sessions that may
* result in an additional STOMP ERROR frame(s) being sent downstream but * result in an additional STOMP ERROR frame(s) being sent downstream but
* code handling that downstream should be idempotent in such cases. * code handling that downstream should be idempotent in such cases.
* * @param message the message to send (never {@code null})
* @param message the message to send, never {@code null}
* @return a future to wait for the result * @return a future to wait for the result
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public ListenableFuture<Void> forward(final Message<?> message) { public ListenableFuture<Void> forward(final Message<?> message) {
TcpConnection<byte[]> conn = this.tcpConnection; TcpConnection<byte[]> conn = this.tcpConnection;
if (!this.isStompConnected) { if (!this.isStompConnected) {
@ -725,12 +708,12 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
if (tcpConnection == null) { if (tcpConnection == null) {
// already reset // already reset
} }
else { else {
handleTcpConnectionFailure("Failed to send message " + message, t); handleTcpConnectionFailure("Failed to send message " + message, ex);
} }
} }
}); });
@ -742,13 +725,11 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
* Close the TCP connection to the broker and release the connection reference, * Close the TCP connection to the broker and release the connection reference,
* Any exception arising from closing the connection is propagated. The caller * Any exception arising from closing the connection is propagated. The caller
* must handle and log the exception accordingly. * must handle and log the exception accordingly.
*
* <p>If the connection belongs to a client session, the connection handler * <p>If the connection belongs to a client session, the connection handler
* for the session (basically the current instance) is also released from the * for the session (basically the current instance) is also released from the
* {@link org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler}. * {@link org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler}.
*/ */
public void clearConnection() { public void clearConnection() {
if (this.isRemoteClientSession) { if (this.isRemoteClientSession) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Removing session '" + sessionId + "' (total remaining=" + logger.debug("Removing session '" + sessionId + "' (total remaining=" +
@ -772,11 +753,11 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
} }
private class SystemStompConnectionHandler extends StompConnectionHandler { private class SystemStompConnectionHandler extends StompConnectionHandler {
public static final String SESSION_ID = "stompRelaySystemSessionId"; public static final String SESSION_ID = "stompRelaySystemSessionId";
public SystemStompConnectionHandler(StompHeaderAccessor connectHeaders) { public SystemStompConnectionHandler(StompHeaderAccessor connectHeaders) {
super(SESSION_ID, connectHeaders, false); super(SESSION_ID, connectHeaders, false);
} }
@ -788,8 +769,8 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
@Override @Override
protected void handleTcpConnectionFailure(String errorMessage, Throwable t) { protected void handleTcpConnectionFailure(String errorMessage, Throwable ex) {
super.handleTcpConnectionFailure(errorMessage, t); super.handleTcpConnectionFailure(errorMessage, ex);
publishBrokerUnavailableEvent(); publishBrokerUnavailableEvent();
} }
@ -806,12 +787,13 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
future.get(); future.get();
return future; return future;
} }
catch (Throwable t) { catch (Throwable ex) {
throw new MessageDeliveryException(message, t); throw new MessageDeliveryException(message, ex);
} }
} }
} }
private static class Reactor11TcpClientFactory { private static class Reactor11TcpClientFactory {
public TcpOperations<byte[]> create(String host, int port) { public TcpOperations<byte[]> create(String host, int port) {
@ -820,6 +802,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
} }
private static class Reactor10TcpClientFactory { private static class Reactor10TcpClientFactory {
public TcpOperations<byte[]> create(String host, int port) { public TcpOperations<byte[]> create(String host, int port) {
@ -828,6 +811,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
} }
} }
private static class VoidCallable implements Callable<Void> { private static class VoidCallable implements Callable<Void> {
@Override @Override

197
spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -88,8 +88,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
*/ */
public AsyncRestTemplate(AsyncListenableTaskExecutor taskExecutor) { public AsyncRestTemplate(AsyncListenableTaskExecutor taskExecutor) {
Assert.notNull(taskExecutor, "AsyncTaskExecutor must not be null"); Assert.notNull(taskExecutor, "AsyncTaskExecutor must not be null");
SimpleClientHttpRequestFactory requestFactory = SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
new SimpleClientHttpRequestFactory();
requestFactory.setTaskExecutor(taskExecutor); requestFactory.setTaskExecutor(taskExecutor);
this.syncTemplate = new RestTemplate(requestFactory); this.syncTemplate = new RestTemplate(requestFactory);
setAsyncRequestFactory(requestFactory); setAsyncRequestFactory(requestFactory);
@ -114,8 +113,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
* @param asyncRequestFactory the asynchronous request factory * @param asyncRequestFactory the asynchronous request factory
* @param syncRequestFactory the synchronous request factory * @param syncRequestFactory the synchronous request factory
*/ */
public AsyncRestTemplate(AsyncClientHttpRequestFactory asyncRequestFactory, public AsyncRestTemplate(AsyncClientHttpRequestFactory asyncRequestFactory, ClientHttpRequestFactory syncRequestFactory) {
ClientHttpRequestFactory syncRequestFactory) {
this(asyncRequestFactory, new RestTemplate(syncRequestFactory)); this(asyncRequestFactory, new RestTemplate(syncRequestFactory));
} }
@ -125,8 +123,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
* @param requestFactory the asynchronous request factory to use * @param requestFactory the asynchronous request factory to use
* @param restTemplate the synchronous template to use * @param restTemplate the synchronous template to use
*/ */
public AsyncRestTemplate(AsyncClientHttpRequestFactory requestFactory, public AsyncRestTemplate(AsyncClientHttpRequestFactory requestFactory, RestTemplate restTemplate) {
RestTemplate restTemplate) {
Assert.notNull(restTemplate, "'restTemplate' must not be null"); Assert.notNull(restTemplate, "'restTemplate' must not be null");
this.syncTemplate = restTemplate; this.syncTemplate = restTemplate;
setAsyncRequestFactory(requestFactory); setAsyncRequestFactory(requestFactory);
@ -195,6 +192,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
return execute(url, HttpMethod.GET, requestCallback, responseExtractor); return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
} }
// HEAD // HEAD
@Override @Override
@ -218,30 +216,29 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// POST // POST
@Override @Override
public ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request, public ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request, Object... uriVariables)
Object... uriVariables) throws RestClientException { throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request); AsyncRequestCallback requestCallback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
ListenableFuture<HttpHeaders> headersFuture = ListenableFuture<HttpHeaders> headersFuture =
execute(url, HttpMethod.POST, requestCallback, headersExtractor, execute(url, HttpMethod.POST, requestCallback, headersExtractor, uriVariables);
uriVariables);
return extractLocationHeader(headersFuture); return extractLocationHeader(headersFuture);
} }
@Override @Override
public ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request, public ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request, Map<String, ?> uriVariables)
Map<String, ?> uriVariables) throws RestClientException { throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request); AsyncRequestCallback requestCallback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
ListenableFuture<HttpHeaders> headersFuture = ListenableFuture<HttpHeaders> headersFuture =
execute(url, HttpMethod.POST, requestCallback, headersExtractor, execute(url, HttpMethod.POST, requestCallback, headersExtractor, uriVariables);
uriVariables);
return extractLocationHeader(headersFuture); return extractLocationHeader(headersFuture);
} }
@Override @Override
public ListenableFuture<URI> postForLocation(URI url, HttpEntity<?> request) public ListenableFuture<URI> postForLocation(URI url, HttpEntity<?> request) throws RestClientException {
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request); AsyncRequestCallback requestCallback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
ListenableFuture<HttpHeaders> headersFuture = ListenableFuture<HttpHeaders> headersFuture =
@ -251,7 +248,6 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
private static ListenableFuture<URI> extractLocationHeader(final ListenableFuture<HttpHeaders> headersFuture) { private static ListenableFuture<URI> extractLocationHeader(final ListenableFuture<HttpHeaders> headersFuture) {
return new ListenableFuture<URI>() { return new ListenableFuture<URI>() {
@Override @Override
public void addCallback(final ListenableFutureCallback<? super URI> callback) { public void addCallback(final ListenableFutureCallback<? super URI> callback) {
headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() { headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() {
@ -259,14 +255,12 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
public void onSuccess(HttpHeaders result) { public void onSuccess(HttpHeaders result) {
callback.onSuccess(result.getLocation()); callback.onSuccess(result.getLocation());
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
callback.onFailure(t); callback.onFailure(ex);
} }
}); });
} }
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) { public boolean cancel(boolean mayInterruptIfRunning) {
return headersFuture.cancel(mayInterruptIfRunning); return headersFuture.cancel(mayInterruptIfRunning);
@ -285,8 +279,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
return headers.getLocation(); return headers.getLocation();
} }
@Override @Override
public URI get(long timeout, TimeUnit unit) public URI get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
throws InterruptedException, ExecutionException, TimeoutException {
HttpHeaders headers = headersFuture.get(timeout, unit); HttpHeaders headers = headersFuture.get(timeout, unit);
return headers.getLocation(); return headers.getLocation();
} }
@ -296,45 +289,41 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request, public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
Class<T> responseType, Object... uriVariables) throws RestClientException { Class<T> responseType, Object... uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType); AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
responseEntityExtractor(responseType); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor,
uriVariables);
} }
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request, public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
Class<T> responseType, Map<String, ?> uriVariables) Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType); AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
responseEntityExtractor(responseType); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor,
uriVariables);
} }
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(URI url, HttpEntity<?> request, public <T> ListenableFuture<ResponseEntity<T>> postForEntity(URI url, HttpEntity<?> request, Class<T> responseType)
Class<T> responseType) throws RestClientException { throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType); AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
responseEntityExtractor(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor); return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
} }
// PUT // PUT
@Override @Override
public ListenableFuture<?> put(String url, HttpEntity<?> request, Object... uriVariables) public ListenableFuture<?> put(String url, HttpEntity<?> request, Object... uriVariables) throws RestClientException {
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request); AsyncRequestCallback requestCallback = httpEntityCallback(request);
return execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); return execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
} }
@Override @Override
public ListenableFuture<?> put(String url, HttpEntity<?> request, public ListenableFuture<?> put(String url, HttpEntity<?> request, Map<String, ?> uriVariables) throws RestClientException {
Map<String, ?> uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request); AsyncRequestCallback requestCallback = httpEntityCallback(request);
return execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); return execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
} }
@ -345,17 +334,16 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
return execute(url, HttpMethod.PUT, requestCallback, null); return execute(url, HttpMethod.PUT, requestCallback, null);
} }
// DELETE // DELETE
@Override @Override
public ListenableFuture<?> delete(String url, Object... urlVariables) public ListenableFuture<?> delete(String url, Object... urlVariables) throws RestClientException {
throws RestClientException {
return execute(url, HttpMethod.DELETE, null, null, urlVariables); return execute(url, HttpMethod.DELETE, null, null, urlVariables);
} }
@Override @Override
public ListenableFuture<?> delete(String url, Map<String, ?> urlVariables) public ListenableFuture<?> delete(String url, Map<String, ?> urlVariables) throws RestClientException {
throws RestClientException {
return execute(url, HttpMethod.DELETE, null, null, urlVariables); return execute(url, HttpMethod.DELETE, null, null, urlVariables);
} }
@ -364,6 +352,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
return execute(url, HttpMethod.DELETE, null, null); return execute(url, HttpMethod.DELETE, null, null);
} }
// OPTIONS // OPTIONS
@Override @Override
@ -389,23 +378,19 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
private static ListenableFuture<Set<HttpMethod>> extractAllowHeader(final ListenableFuture<HttpHeaders> headersFuture) { private static ListenableFuture<Set<HttpMethod>> extractAllowHeader(final ListenableFuture<HttpHeaders> headersFuture) {
return new ListenableFuture<Set<HttpMethod>>() { return new ListenableFuture<Set<HttpMethod>>() {
@Override @Override
public void addCallback( public void addCallback(final ListenableFutureCallback<? super Set<HttpMethod>> callback) {
final ListenableFutureCallback<? super Set<HttpMethod>> callback) {
headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() { headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() {
@Override @Override
public void onSuccess(HttpHeaders result) { public void onSuccess(HttpHeaders result) {
callback.onSuccess(result.getAllow()); callback.onSuccess(result.getAllow());
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
callback.onFailure(t); callback.onFailure(ex);
} }
}); });
} }
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) { public boolean cancel(boolean mayInterruptIfRunning) {
return headersFuture.cancel(mayInterruptIfRunning); return headersFuture.cancel(mayInterruptIfRunning);
@ -424,8 +409,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
return headers.getAllow(); return headers.getAllow();
} }
@Override @Override
public Set<HttpMethod> get(long timeout, TimeUnit unit) public Set<HttpMethod> get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
throws InterruptedException, ExecutionException, TimeoutException {
HttpHeaders headers = headersFuture.get(timeout, unit); HttpHeaders headers = headersFuture.get(timeout, unit);
return headers.getAllow(); return headers.getAllow();
} }
@ -436,68 +420,59 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// exchange // exchange
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) Class<T> responseType, Object... uriVariables) throws RestClientException {
throws RestClientException {
AsyncRequestCallback requestCallback = AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
httpEntityCallback(requestEntity, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor =
responseEntityExtractor(responseType);
return execute(url, method, requestCallback, responseExtractor, uriVariables); return execute(url, method, requestCallback, responseExtractor, uriVariables);
} }
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
HttpEntity<?> requestEntity, Class<T> responseType, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
Map<String, ?> uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
httpEntityCallback(requestEntity, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor =
responseEntityExtractor(responseType);
return execute(url, method, requestCallback, responseExtractor, uriVariables); return execute(url, method, requestCallback, responseExtractor, uriVariables);
} }
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method, public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
HttpEntity<?> requestEntity, Class<T> responseType) Class<T> responseType) throws RestClientException {
throws RestClientException {
AsyncRequestCallback requestCallback = AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
httpEntityCallback(requestEntity, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor =
responseEntityExtractor(responseType);
return execute(url, method, requestCallback, responseExtractor); return execute(url, method, requestCallback, responseExtractor);
} }
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
Object... uriVariables) throws RestClientException {
Type type = responseType.getType(); Type type = responseType.getType();
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type); AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
responseEntityExtractor(type);
return execute(url, method, requestCallback, responseExtractor, uriVariables); return execute(url, method, requestCallback, responseExtractor, uriVariables);
} }
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
Map<String, ?> uriVariables) throws RestClientException {
Type type = responseType.getType(); Type type = responseType.getType();
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type); AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
responseEntityExtractor(type);
return execute(url, method, requestCallback, responseExtractor, uriVariables); return execute(url, method, requestCallback, responseExtractor, uriVariables);
} }
@Override @Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method, public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType) ParameterizedTypeReference<T> responseType) throws RestClientException {
throws RestClientException {
Type type = responseType.getType(); Type type = responseType.getType();
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type); AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
responseEntityExtractor(type);
return execute(url, method, requestCallback, responseExtractor); return execute(url, method, requestCallback, responseExtractor);
} }
@ -505,27 +480,24 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// general execution // general execution
@Override @Override
public <T> ListenableFuture<T> execute(String url, HttpMethod method, public <T> ListenableFuture<T> execute(String url, HttpMethod method, AsyncRequestCallback requestCallback,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor, ResponseExtractor<T> responseExtractor, Object... urlVariables) throws RestClientException {
Object... urlVariables) throws RestClientException {
URI expanded = new UriTemplate(url).expand(urlVariables); URI expanded = new UriTemplate(url).expand(urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor); return doExecute(expanded, method, requestCallback, responseExtractor);
} }
@Override @Override
public <T> ListenableFuture<T> execute(String url, HttpMethod method, public <T> ListenableFuture<T> execute(String url, HttpMethod method, AsyncRequestCallback requestCallback,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor, ResponseExtractor<T> responseExtractor, Map<String, ?> urlVariables) throws RestClientException {
Map<String, ?> urlVariables) throws RestClientException {
URI expanded = new UriTemplate(url).expand(urlVariables); URI expanded = new UriTemplate(url).expand(urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor); return doExecute(expanded, method, requestCallback, responseExtractor);
} }
@Override @Override
public <T> ListenableFuture<T> execute(URI url, HttpMethod method, public <T> ListenableFuture<T> execute(URI url, HttpMethod method, AsyncRequestCallback requestCallback,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor) ResponseExtractor<T> responseExtractor) throws RestClientException {
throws RestClientException {
return doExecute(url, method, requestCallback, responseExtractor); return doExecute(url, method, requestCallback, responseExtractor);
} }
@ -553,8 +525,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
requestCallback.doWithRequest(request); requestCallback.doWithRequest(request);
} }
ListenableFuture<ClientHttpResponse> responseFuture = request.executeAsync(); ListenableFuture<ClientHttpResponse> responseFuture = request.executeAsync();
return new ResponseExtractorFuture<T>(method, url, responseFuture, return new ResponseExtractorFuture<T>(method, url, responseFuture, responseExtractor);
responseExtractor);
} }
catch (IOException ex) { catch (IOException ex) {
throw new ResourceAccessException("I/O error on " + method.name() + throw new ResourceAccessException("I/O error on " + method.name() +
@ -565,9 +536,8 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) { private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
try { try {
logger.debug("Async " + method.name() + " request for \"" + url + logger.debug("Async " + method.name() + " request for \"" + url + "\" resulted in " +
"\" resulted in " + response.getStatusCode() + " (" + response.getStatusCode() + " (" + response.getStatusText() + ")");
response.getStatusText() + ")");
} }
catch (IOException ex) { catch (IOException ex) {
// ignore // ignore
@ -578,9 +548,8 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException { private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) { if (logger.isWarnEnabled()) {
try { try {
logger.warn("Async " + method.name() + " request for \"" + url + logger.warn("Async " + method.name() + " request for \"" + url + "\" resulted in " +
"\" resulted in " + response.getStatusCode() + " (" + response.getStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
response.getStatusText() + "); invoking error handler");
} }
catch (IOException ex) { catch (IOException ex) {
// ignore // ignore
@ -628,12 +597,12 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
return this.syncTemplate.headersExtractor(); return this.syncTemplate.headersExtractor();
} }
/** /**
* Future returned from * Future returned from
* {@link #doExecute(URI, HttpMethod, AsyncRequestCallback, ResponseExtractor)} * {@link #doExecute(URI, HttpMethod, AsyncRequestCallback, ResponseExtractor)}
*/ */
private class ResponseExtractorFuture<T> private class ResponseExtractorFuture<T> extends ListenableFutureAdapter<T, ClientHttpResponse> {
extends ListenableFutureAdapter<T, ClientHttpResponse> {
private final HttpMethod method; private final HttpMethod method;
@ -642,8 +611,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
private final ResponseExtractor<T> responseExtractor; private final ResponseExtractor<T> responseExtractor;
public ResponseExtractorFuture(HttpMethod method, URI url, public ResponseExtractorFuture(HttpMethod method, URI url,
ListenableFuture<ClientHttpResponse> clientHttpResponseFuture, ListenableFuture<ClientHttpResponse> clientHttpResponseFuture, ResponseExtractor<T> responseExtractor) {
ResponseExtractor<T> responseExtractor) {
super(clientHttpResponseFuture); super(clientHttpResponseFuture);
this.method = method; this.method = method;
this.url = url; this.url = url;
@ -672,12 +640,11 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
} }
protected T convertResponse(ClientHttpResponse response) throws IOException { protected T convertResponse(ClientHttpResponse response) throws IOException {
return responseExtractor != null ? responseExtractor.extractData(response) : return (this.responseExtractor != null ? this.responseExtractor.extractData(response) : null);
null;
} }
} }
/** /**
* Adapts a {@link RequestCallback} to the {@link AsyncRequestCallback} interface. * Adapts a {@link RequestCallback} to the {@link AsyncRequestCallback} interface.
*/ */

81
spring-web/src/test/java/org/springframework/http/client/AbstractAsyncHttpRequestFactoryTestCase.java

@ -37,11 +37,11 @@ import org.springframework.util.concurrent.ListenableFutureCallback;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public abstract class AbstractAsyncHttpRequestFactoryTestCase extends public abstract class AbstractAsyncHttpRequestFactoryTestCase extends AbstractJettyServerTestCase {
AbstractJettyServerTestCase {
protected AsyncClientHttpRequestFactory factory; protected AsyncClientHttpRequestFactory factory;
@Before @Before
public final void createFactory() throws Exception { public final void createFactory() throws Exception {
factory = createRequestFactory(); factory = createRequestFactory();
@ -52,6 +52,7 @@ public abstract class AbstractAsyncHttpRequestFactoryTestCase extends
protected abstract AsyncClientHttpRequestFactory createRequestFactory(); protected abstract AsyncClientHttpRequestFactory createRequestFactory();
@Test @Test
public void status() throws Exception { public void status() throws Exception {
URI uri = new URI(baseUrl + "/status/notfound"); URI uri = new URI(baseUrl + "/status/notfound");
@ -60,8 +61,7 @@ public abstract class AbstractAsyncHttpRequestFactoryTestCase extends
assertEquals("Invalid HTTP URI", uri, request.getURI()); assertEquals("Invalid HTTP URI", uri, request.getURI());
Future<ClientHttpResponse> futureResponse = request.executeAsync(); Future<ClientHttpResponse> futureResponse = request.executeAsync();
ClientHttpResponse response = futureResponse.get(); ClientHttpResponse response = futureResponse.get();
assertEquals("Invalid status code", HttpStatus.NOT_FOUND, assertEquals("Invalid status code", HttpStatus.NOT_FOUND, response.getStatusCode());
response.getStatusCode());
} }
@Test @Test
@ -70,46 +70,34 @@ public abstract class AbstractAsyncHttpRequestFactoryTestCase extends
AsyncClientHttpRequest request = factory.createAsyncRequest(uri, HttpMethod.GET); AsyncClientHttpRequest request = factory.createAsyncRequest(uri, HttpMethod.GET);
assertEquals("Invalid HTTP method", HttpMethod.GET, request.getMethod()); assertEquals("Invalid HTTP method", HttpMethod.GET, request.getMethod());
assertEquals("Invalid HTTP URI", uri, request.getURI()); assertEquals("Invalid HTTP URI", uri, request.getURI());
Future<ClientHttpResponse> futureResponse = request.executeAsync(); ListenableFuture<ClientHttpResponse> listenableFuture = request.executeAsync();
if (futureResponse instanceof ListenableFuture) { listenableFuture.addCallback(new ListenableFutureCallback<ClientHttpResponse>() {
ListenableFuture<ClientHttpResponse> listenableFuture = @Override
(ListenableFuture<ClientHttpResponse>) futureResponse; public void onSuccess(ClientHttpResponse result) {
try {
System.out.println("SUCCESS! " + result.getStatusCode());
listenableFuture.addCallback(new ListenableFutureCallback<ClientHttpResponse>() { System.out.println("Callback: " + System.currentTimeMillis());
@Override System.out.println(Thread.currentThread().getId());
public void onSuccess(ClientHttpResponse result) { assertEquals("Invalid status code", HttpStatus.NOT_FOUND, result.getStatusCode());
try {
System.out.println("SUCCESS! " + result.getStatusCode());
System.out.println("Callback: " + System.currentTimeMillis());
System.out.println(Thread.currentThread().getId());
assertEquals("Invalid status code", HttpStatus.NOT_FOUND,
result.getStatusCode());
}
catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
} }
catch (IOException ex) {
@Override ex.printStackTrace();
public void onFailure(Throwable t) {
System.out.println("FAILURE: " + t);
} }
}); }
@Override
} public void onFailure(Throwable ex) {
ClientHttpResponse response = futureResponse.get(); System.out.println("FAILURE: " + ex);
}
});
ClientHttpResponse response = listenableFuture.get();
System.out.println("Main thread: " + System.currentTimeMillis()); System.out.println("Main thread: " + System.currentTimeMillis());
assertEquals("Invalid status code", HttpStatus.NOT_FOUND, assertEquals("Invalid status code", HttpStatus.NOT_FOUND, response.getStatusCode());
response.getStatusCode());
System.out.println(Thread.currentThread().getId()); System.out.println(Thread.currentThread().getId());
} }
@Test @Test
public void echo() throws Exception { public void echo() throws Exception {
AsyncClientHttpRequest AsyncClientHttpRequest request = factory.createAsyncRequest(new URI(baseUrl + "/echo"), HttpMethod.PUT);
request = factory.createAsyncRequest(new URI(baseUrl + "/echo"),
HttpMethod.PUT);
assertEquals("Invalid HTTP method", HttpMethod.PUT, request.getMethod()); assertEquals("Invalid HTTP method", HttpMethod.PUT, request.getMethod());
String headerName = "MyHeader"; String headerName = "MyHeader";
String headerValue1 = "value1"; String headerValue1 = "value1";
@ -118,9 +106,9 @@ public abstract class AbstractAsyncHttpRequestFactoryTestCase extends
request.getHeaders().add(headerName, headerValue2); request.getHeaders().add(headerName, headerValue2);
final byte[] body = "Hello World".getBytes("UTF-8"); final byte[] body = "Hello World".getBytes("UTF-8");
request.getHeaders().setContentLength(body.length); request.getHeaders().setContentLength(body.length);
if (request instanceof StreamingHttpOutputMessage) { if (request instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingRequest = StreamingHttpOutputMessage streamingRequest = (StreamingHttpOutputMessage) request;
(StreamingHttpOutputMessage) request;
streamingRequest.setBody(new StreamingHttpOutputMessage.Body() { streamingRequest.setBody(new StreamingHttpOutputMessage.Body() {
@Override @Override
public void writeTo(OutputStream outputStream) throws IOException { public void writeTo(OutputStream outputStream) throws IOException {
@ -131,6 +119,7 @@ public abstract class AbstractAsyncHttpRequestFactoryTestCase extends
else { else {
StreamUtils.copy(body, request.getBody()); StreamUtils.copy(body, request.getBody());
} }
Future<ClientHttpResponse> futureResponse = request.executeAsync(); Future<ClientHttpResponse> futureResponse = request.executeAsync();
ClientHttpResponse response = futureResponse.get(); ClientHttpResponse response = futureResponse.get();
try { try {
@ -148,13 +137,11 @@ public abstract class AbstractAsyncHttpRequestFactoryTestCase extends
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void multipleWrites() throws Exception { public void multipleWrites() throws Exception {
AsyncClientHttpRequest AsyncClientHttpRequest request = factory.createAsyncRequest(new URI(baseUrl + "/echo"), HttpMethod.POST);
request = factory.createAsyncRequest(new URI(baseUrl + "/echo"),
HttpMethod.POST);
final byte[] body = "Hello World".getBytes("UTF-8"); final byte[] body = "Hello World".getBytes("UTF-8");
if (request instanceof StreamingHttpOutputMessage) { if (request instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingRequest = StreamingHttpOutputMessage streamingRequest = (StreamingHttpOutputMessage) request;
(StreamingHttpOutputMessage) request;
streamingRequest.setBody(new StreamingHttpOutputMessage.Body() { streamingRequest.setBody(new StreamingHttpOutputMessage.Body() {
@Override @Override
public void writeTo(OutputStream outputStream) throws IOException { public void writeTo(OutputStream outputStream) throws IOException {
@ -178,9 +165,7 @@ public abstract class AbstractAsyncHttpRequestFactoryTestCase extends
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void headersAfterExecute() throws Exception { public void headersAfterExecute() throws Exception {
AsyncClientHttpRequest AsyncClientHttpRequest request = factory.createAsyncRequest(new URI(baseUrl + "/echo"), HttpMethod.POST);
request = factory.createAsyncRequest(new URI(baseUrl + "/echo"),
HttpMethod.POST);
request.getHeaders().add("MyHeader", "value"); request.getHeaders().add("MyHeader", "value");
byte[] body = "Hello World".getBytes("UTF-8"); byte[] body = "Hello World".getBytes("UTF-8");
FileCopyUtils.copy(body, request.getBody()); FileCopyUtils.copy(body, request.getBody());
@ -208,9 +193,7 @@ public abstract class AbstractAsyncHttpRequestFactoryTestCase extends
protected void assertHttpMethod(String path, HttpMethod method) throws Exception { protected void assertHttpMethod(String path, HttpMethod method) throws Exception {
ClientHttpResponse response = null; ClientHttpResponse response = null;
try { try {
AsyncClientHttpRequest request = factory.createAsyncRequest( AsyncClientHttpRequest request = factory.createAsyncRequest(new URI(baseUrl + "/methods/" + path), method);
new URI(baseUrl + "/methods/" + path), method);
Future<ClientHttpResponse> futureResponse = request.executeAsync(); Future<ClientHttpResponse> futureResponse = request.executeAsync();
response = futureResponse.get(); response = futureResponse.get();
assertEquals("Invalid response status", HttpStatus.OK, response.getStatusCode()); assertEquals("Invalid response status", HttpStatus.OK, response.getStatusCode());

225
spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java

@ -16,16 +16,12 @@
package org.springframework.web.client; package org.springframework.web.client;
import java.io.UnsupportedEncodingException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import static org.junit.Assert.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -43,23 +39,27 @@ import org.springframework.util.MultiValueMap;
import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.util.concurrent.ListenableFutureCallback;
import static org.junit.Assert.*;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Sebastien Deleuze
*/ */
public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCase { public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCase {
private AsyncRestTemplate template; private AsyncRestTemplate template;
@Before @Before
public void createTemplate() { public void createTemplate() {
template = new AsyncRestTemplate( template = new AsyncRestTemplate(new HttpComponentsAsyncClientHttpRequestFactory());
new HttpComponentsAsyncClientHttpRequestFactory());
} }
@Test @Test
public void getEntity() throws ExecutionException, InterruptedException { public void getEntity() throws Exception {
Future<ResponseEntity<String>> Future<ResponseEntity<String>> futureEntity =
futureEntity = template.getForEntity(baseUrl + "/{method}", String.class, "get"); template.getForEntity(baseUrl + "/{method}", String.class, "get");
ResponseEntity<String> entity = futureEntity.get(); ResponseEntity<String> entity = futureEntity.get();
assertEquals("Invalid content", helloWorld, entity.getBody()); assertEquals("Invalid content", helloWorld, entity.getBody());
assertFalse("No headers", entity.getHeaders().isEmpty()); assertFalse("No headers", entity.getHeaders().isEmpty());
@ -68,17 +68,17 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void multipleFutureGets() throws ExecutionException, InterruptedException { public void multipleFutureGets() throws Exception {
Future<ResponseEntity<String>> Future<ResponseEntity<String>> futureEntity =
futureEntity = template.getForEntity(baseUrl + "/{method}", String.class, "get"); template.getForEntity(baseUrl + "/{method}", String.class, "get");
futureEntity.get(); futureEntity.get();
futureEntity.get(); futureEntity.get();
} }
@Test @Test
public void getEntityCallback() throws ExecutionException, InterruptedException { public void getEntityCallback() throws Exception {
ListenableFuture<ResponseEntity<String>> ListenableFuture<ResponseEntity<String>> futureEntity =
futureEntity = template.getForEntity(baseUrl + "/{method}", String.class, "get"); template.getForEntity(baseUrl + "/{method}", String.class, "get");
futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() { futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override @Override
public void onSuccess(ResponseEntity<String> entity) { public void onSuccess(ResponseEntity<String> entity) {
@ -87,10 +87,9 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
assertEquals("Invalid content-type", contentType, entity.getHeaders().getContentType()); assertEquals("Invalid content-type", contentType, entity.getHeaders().getContentType());
assertEquals("Invalid status code", HttpStatus.OK, entity.getStatusCode()); assertEquals("Invalid status code", HttpStatus.OK, entity.getStatusCode());
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
// wait till done // wait till done
@ -99,65 +98,55 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void getNoResponse() throws ExecutionException, InterruptedException { public void getNoResponse() throws Exception {
Future<ResponseEntity<String>> Future<ResponseEntity<String>> futureEntity = template.getForEntity(baseUrl + "/get/nothing", String.class);
futureEntity = template.getForEntity(baseUrl + "/get/nothing", String.class);
ResponseEntity<String> entity = futureEntity.get(); ResponseEntity<String> entity = futureEntity.get();
assertNull("Invalid content", entity.getBody()); assertNull("Invalid content", entity.getBody());
} }
@Test @Test
public void getNoContentTypeHeader() public void getNoContentTypeHeader() throws Exception {
throws UnsupportedEncodingException, ExecutionException, Future<ResponseEntity<byte[]>> futureEntity = template.getForEntity(baseUrl + "/get/nocontenttype", byte[].class);
InterruptedException {
Future<ResponseEntity<byte[]>>
futureEntity = template.getForEntity(baseUrl + "/get/nocontenttype",
byte[].class);
ResponseEntity<byte[]> responseEntity = futureEntity.get(); ResponseEntity<byte[]> responseEntity = futureEntity.get();
assertArrayEquals("Invalid content", helloWorld.getBytes("UTF-8"), assertArrayEquals("Invalid content", helloWorld.getBytes("UTF-8"), responseEntity.getBody());
responseEntity.getBody());
} }
@Test @Test
public void getNoContent() throws ExecutionException, InterruptedException { public void getNoContent() throws Exception {
Future<ResponseEntity<String>> Future<ResponseEntity<String>> responseFuture = template.getForEntity(baseUrl + "/status/nocontent", String.class);
responseFuture = template.getForEntity(baseUrl + "/status/nocontent", String.class);
ResponseEntity<String> entity = responseFuture.get(); ResponseEntity<String> entity = responseFuture.get();
assertEquals("Invalid response code", HttpStatus.NO_CONTENT, entity.getStatusCode()); assertEquals("Invalid response code", HttpStatus.NO_CONTENT, entity.getStatusCode());
assertNull("Invalid content", entity.getBody()); assertNull("Invalid content", entity.getBody());
} }
@Test @Test
public void getNotModified() throws ExecutionException, InterruptedException { public void getNotModified() throws Exception {
Future<ResponseEntity<String>> Future<ResponseEntity<String>> responseFuture = template.getForEntity(baseUrl + "/status/notmodified", String.class);
responseFuture = template.getForEntity(baseUrl + "/status/notmodified",
String.class);
ResponseEntity<String> entity = responseFuture.get(); ResponseEntity<String> entity = responseFuture.get();
assertEquals("Invalid response code", HttpStatus.NOT_MODIFIED, entity.getStatusCode()); assertEquals("Invalid response code", HttpStatus.NOT_MODIFIED, entity.getStatusCode());
assertNull("Invalid content", entity.getBody()); assertNull("Invalid content", entity.getBody());
} }
@Test @Test
public void headForHeaders() throws ExecutionException, InterruptedException { public void headForHeaders() throws Exception {
Future<HttpHeaders> headersFuture = template.headForHeaders(baseUrl + "/get"); Future<HttpHeaders> headersFuture = template.headForHeaders(baseUrl + "/get");
HttpHeaders headers = headersFuture.get(); HttpHeaders headers = headersFuture.get();
assertTrue("No Content-Type header", headers.containsKey("Content-Type")); assertTrue("No Content-Type header", headers.containsKey("Content-Type"));
} }
@Test @Test
public void headForHeadersCallback() throws ExecutionException, InterruptedException { public void headForHeadersCallback() throws Exception {
ListenableFuture<HttpHeaders> headersFuture = template.headForHeaders(baseUrl + "/get"); ListenableFuture<HttpHeaders> headersFuture = template.headForHeaders(baseUrl + "/get");
headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() { headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() {
@Override @Override
public void onSuccess(HttpHeaders result) { public void onSuccess(HttpHeaders result) {
assertTrue("No Content-Type header", result.containsKey("Content-Type")); assertTrue("No Content-Type header", result.containsKey("Content-Type"));
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
while (!headersFuture.isDone()) { while (!headersFuture.isDone()) {
@ -165,37 +154,30 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void postForLocation() public void postForLocation() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException {
HttpHeaders entityHeaders = new HttpHeaders(); HttpHeaders entityHeaders = new HttpHeaders();
entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15")));
HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders); HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders);
Future<URI> Future<URI> locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post");
locationFuture = template.postForLocation(baseUrl + "/{method}", entity,
"post");
URI location = locationFuture.get(); URI location = locationFuture.get();
assertEquals("Invalid location", new URI(baseUrl + "/post/1"), location); assertEquals("Invalid location", new URI(baseUrl + "/post/1"), location);
} }
@Test @Test
public void postForLocationCallback() public void postForLocationCallback() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException {
HttpHeaders entityHeaders = new HttpHeaders(); HttpHeaders entityHeaders = new HttpHeaders();
entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15")));
HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders); HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders);
final URI expected = new URI(baseUrl + "/post/1"); final URI expected = new URI(baseUrl + "/post/1");
ListenableFuture<URI> ListenableFuture<URI> locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post");
locationFuture = template.postForLocation(baseUrl + "/{method}", entity,
"post");
locationFuture.addCallback(new ListenableFutureCallback<URI>() { locationFuture.addCallback(new ListenableFutureCallback<URI>() {
@Override @Override
public void onSuccess(URI result) { public void onSuccess(URI result) {
assertEquals("Invalid location", expected, result); assertEquals("Invalid location", expected, result);
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
while (!locationFuture.isDone()) { while (!locationFuture.isDone()) {
@ -203,32 +185,27 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void postForEntity() public void postForEntity() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld); HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
Future<ResponseEntity<String>> Future<ResponseEntity<String>> responseEntityFuture =
responseEntityFuture = template.postForEntity(baseUrl + "/{method}", requestEntity, template.postForEntity(baseUrl + "/{method}", requestEntity, String.class, "post");
String.class, "post");
ResponseEntity<String> responseEntity = responseEntityFuture.get(); ResponseEntity<String> responseEntity = responseEntityFuture.get();
assertEquals("Invalid content", helloWorld, responseEntity.getBody()); assertEquals("Invalid content", helloWorld, responseEntity.getBody());
} }
@Test @Test
public void postForEntityCallback() public void postForEntityCallback() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld); HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
ListenableFuture<ResponseEntity<String>> ListenableFuture<ResponseEntity<String>> responseEntityFuture =
responseEntityFuture = template.postForEntity(baseUrl + "/{method}", requestEntity, template.postForEntity(baseUrl + "/{method}", requestEntity, String.class, "post");
String.class, "post");
responseEntityFuture.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() { responseEntityFuture.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override @Override
public void onSuccess(ResponseEntity<String> result) { public void onSuccess(ResponseEntity<String> result) {
assertEquals("Invalid content", helloWorld, result.getBody()); assertEquals("Invalid content", helloWorld, result.getBody());
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
while (!responseEntityFuture.isDone()) { while (!responseEntityFuture.isDone()) {
@ -236,31 +213,24 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void put() public void put() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld); HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
Future<?> Future<?> responseEntityFuture = template.put(baseUrl + "/{method}", requestEntity, "put");
responseEntityFuture = template.put(baseUrl + "/{method}", requestEntity,
"put");
responseEntityFuture.get(); responseEntityFuture.get();
} }
@Test @Test
public void putCallback() public void putCallback() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld); HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
ListenableFuture<?> ListenableFuture<?> responseEntityFuture = template.put(baseUrl + "/{method}", requestEntity, "put");
responseEntityFuture = template.put(baseUrl + "/{method}", requestEntity,
"put");
responseEntityFuture.addCallback(new ListenableFutureCallback<Object>() { responseEntityFuture.addCallback(new ListenableFutureCallback<Object>() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
assertNull(result); assertNull(result);
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
while (!responseEntityFuture.isDone()) { while (!responseEntityFuture.isDone()) {
@ -268,26 +238,22 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void delete() public void delete() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException {
Future<?> deletedFuture = template.delete(new URI(baseUrl + "/delete")); Future<?> deletedFuture = template.delete(new URI(baseUrl + "/delete"));
deletedFuture.get(); deletedFuture.get();
} }
@Test @Test
public void deleteCallback() public void deleteCallback() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException {
ListenableFuture<?> deletedFuture = template.delete(new URI(baseUrl + "/delete")); ListenableFuture<?> deletedFuture = template.delete(new URI(baseUrl + "/delete"));
deletedFuture.addCallback(new ListenableFutureCallback<Object>() { deletedFuture.addCallback(new ListenableFutureCallback<Object>() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
assertNull(result); assertNull(result);
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
while (!deletedFuture.isDone()) { while (!deletedFuture.isDone()) {
@ -295,7 +261,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void notFound() throws ExecutionException, InterruptedException { public void notFound() throws Exception {
try { try {
Future<?> future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null); Future<?> future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null);
future.get(); future.get();
@ -309,16 +275,13 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void notFoundCallback() throws ExecutionException, InterruptedException { public void notFoundCallback() throws Exception {
ListenableFuture<?> future = ListenableFuture<?> future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null);
template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null,
null);
future.addCallback(new ListenableFutureCallback<Object>() { future.addCallback(new ListenableFutureCallback<Object>() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
fail("onSuccess not expected"); fail("onSuccess not expected");
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
assertTrue(t instanceof HttpClientErrorException); assertTrue(t instanceof HttpClientErrorException);
@ -333,7 +296,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void serverError() throws ExecutionException, InterruptedException { public void serverError() throws Exception {
try { try {
Future<Void> future = template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null); Future<Void> future = template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null);
future.get(); future.get();
@ -347,21 +310,20 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void serverErrorCallback() throws ExecutionException, InterruptedException { public void serverErrorCallback() throws Exception {
ListenableFuture<Void> future = template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null); ListenableFuture<Void> future = template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null);
future.addCallback(new ListenableFutureCallback<Void>() { future.addCallback(new ListenableFutureCallback<Void>() {
@Override @Override
public void onSuccess(Void result) { public void onSuccess(Void result) {
fail("onSuccess not expected"); fail("onSuccess not expected");
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
assertTrue(t instanceof HttpServerErrorException); assertTrue(ex instanceof HttpServerErrorException);
HttpServerErrorException ex = (HttpServerErrorException) t; HttpServerErrorException hsex = (HttpServerErrorException) ex;
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, ex.getStatusCode()); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, hsex.getStatusCode());
assertNotNull(ex.getStatusText()); assertNotNull(hsex.getStatusText());
assertNotNull(ex.getResponseBodyAsString()); assertNotNull(hsex.getResponseBodyAsString());
} }
}); });
while (!future.isDone()) { while (!future.isDone()) {
@ -369,30 +331,25 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
} }
@Test @Test
public void optionsForAllow() public void optionsForAllow() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException { Future<Set<HttpMethod>> allowedFuture = template.optionsForAllow(new URI(baseUrl + "/get"));
Future<Set<HttpMethod>>
allowedFuture = template.optionsForAllow(new URI(baseUrl + "/get"));
Set<HttpMethod> allowed = allowedFuture.get(); Set<HttpMethod> allowed = allowedFuture.get();
assertEquals("Invalid response", assertEquals("Invalid response",
EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE), allowed); EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE), allowed);
} }
@Test @Test
public void optionsForAllowCallback() public void optionsForAllowCallback() throws Exception {
throws URISyntaxException, ExecutionException, InterruptedException { ListenableFuture<Set<HttpMethod>> allowedFuture = template.optionsForAllow(new URI(baseUrl + "/get"));
ListenableFuture<Set<HttpMethod>>
allowedFuture = template.optionsForAllow(new URI(baseUrl + "/get"));
allowedFuture.addCallback(new ListenableFutureCallback<Set<HttpMethod>>() { allowedFuture.addCallback(new ListenableFutureCallback<Set<HttpMethod>>() {
@Override @Override
public void onSuccess(Set<HttpMethod> result) { public void onSuccess(Set<HttpMethod> result) {
assertEquals("Invalid response", assertEquals("Invalid response", EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS,
EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE), result); HttpMethod.HEAD, HttpMethod.TRACE), result);
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
while (!allowedFuture.isDone()) { while (!allowedFuture.isDone()) {
@ -406,8 +363,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
requestHeaders.set("MyHeader", "MyValue"); requestHeaders.set("MyHeader", "MyValue");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders); HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
Future<ResponseEntity<String>> responseFuture = Future<ResponseEntity<String>> responseFuture =
template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, String.class, "get");
String.class, "get");
ResponseEntity<String> response = responseFuture.get(); ResponseEntity<String> response = responseFuture.get();
assertEquals("Invalid content", helloWorld, response.getBody()); assertEquals("Invalid content", helloWorld, response.getBody());
} }
@ -419,17 +375,15 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
requestHeaders.set("MyHeader", "MyValue"); requestHeaders.set("MyHeader", "MyValue");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders); HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
ListenableFuture<ResponseEntity<String>> responseFuture = ListenableFuture<ResponseEntity<String>> responseFuture =
template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, String.class, "get");
String.class, "get");
responseFuture.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() { responseFuture.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override @Override
public void onSuccess(ResponseEntity<String> result) { public void onSuccess(ResponseEntity<String> result) {
assertEquals("Invalid content", helloWorld, result.getBody()); assertEquals("Invalid content", helloWorld, result.getBody());
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
while (!responseFuture.isDone()) { while (!responseFuture.isDone()) {
@ -442,9 +396,8 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
requestHeaders.set("MyHeader", "MyValue"); requestHeaders.set("MyHeader", "MyValue");
requestHeaders.setContentType(MediaType.TEXT_PLAIN); requestHeaders.setContentType(MediaType.TEXT_PLAIN);
HttpEntity<String> requestEntity = new HttpEntity<String>(helloWorld, requestHeaders); HttpEntity<String> requestEntity = new HttpEntity<String>(helloWorld, requestHeaders);
Future<ResponseEntity<Void>> Future<ResponseEntity<Void>> resultFuture =
resultFuture = template.exchange(baseUrl + "/{method}", HttpMethod.POST, template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, Void.class, "post");
requestEntity, Void.class, "post");
ResponseEntity<Void> result = resultFuture.get(); ResponseEntity<Void> result = resultFuture.get();
assertEquals("Invalid location", new URI(baseUrl + "/post/1"), assertEquals("Invalid location", new URI(baseUrl + "/post/1"),
result.getHeaders().getLocation()); result.getHeaders().getLocation());
@ -457,31 +410,26 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
requestHeaders.set("MyHeader", "MyValue"); requestHeaders.set("MyHeader", "MyValue");
requestHeaders.setContentType(MediaType.TEXT_PLAIN); requestHeaders.setContentType(MediaType.TEXT_PLAIN);
HttpEntity<String> requestEntity = new HttpEntity<String>(helloWorld, requestHeaders); HttpEntity<String> requestEntity = new HttpEntity<String>(helloWorld, requestHeaders);
ListenableFuture<ResponseEntity<Void>> ListenableFuture<ResponseEntity<Void>> resultFuture =
resultFuture = template.exchange(baseUrl + "/{method}", HttpMethod.POST, template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, Void.class, "post");
requestEntity, Void.class, "post");
final URI expected =new URI(baseUrl + "/post/1"); final URI expected =new URI(baseUrl + "/post/1");
resultFuture.addCallback(new ListenableFutureCallback<ResponseEntity<Void>>() { resultFuture.addCallback(new ListenableFutureCallback<ResponseEntity<Void>>() {
@Override @Override
public void onSuccess(ResponseEntity<Void> result) { public void onSuccess(ResponseEntity<Void> result) {
assertEquals("Invalid location", expected, assertEquals("Invalid location", expected, result.getHeaders().getLocation());
result.getHeaders().getLocation());
assertFalse(result.hasBody()); assertFalse(result.hasBody());
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
fail(t.getMessage()); fail(ex.getMessage());
} }
}); });
while (!resultFuture.isDone()) { while (!resultFuture.isDone()) {
} }
} }
@Test @Test
public void multipart() throws UnsupportedEncodingException, ExecutionException, public void multipart() throws Exception {
InterruptedException {
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>(); MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.add("name 1", "value 1"); parts.add("name 1", "value 1");
parts.add("name 2", "value 2+1"); parts.add("name 2", "value 2+1");
@ -490,8 +438,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
parts.add("logo", logo); parts.add("logo", logo);
HttpEntity<MultiValueMap<String, Object>> requestBody = new HttpEntity<>(parts); HttpEntity<MultiValueMap<String, Object>> requestBody = new HttpEntity<>(parts);
Future<URI> future = Future<URI> future = template.postForLocation(baseUrl + "/multipart", requestBody);
template.postForLocation(baseUrl + "/multipart", requestBody);
future.get(); future.get();
} }

17
spring-websocket/src/main/java/org/springframework/web/socket/client/WebSocketConnectionManager.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,9 +23,9 @@ import org.springframework.http.HttpHeaders;
import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator; import org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator;
import org.springframework.web.socket.WebSocketHttpHeaders;
/** /**
* A WebSocket connection manager that is given a URI, a {@link WebSocketClient}, and a * A WebSocket connection manager that is given a URI, a {@link WebSocketClient}, and a
@ -55,7 +55,7 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport {
super(uriTemplate, uriVariables); super(uriTemplate, uriVariables);
this.client = client; this.client = client;
this.webSocketHandler = decorateWebSocketHandler(webSocketHandler); this.webSocketHandler = decorateWebSocketHandler(webSocketHandler);
this.syncClientLifecycle = ((client instanceof SmartLifecycle) && !((SmartLifecycle) client).isRunning()); this.syncClientLifecycle = (client instanceof SmartLifecycle && !((SmartLifecycle) client).isRunning());
} }
@ -132,8 +132,9 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport {
@Override @Override
protected void openConnection() { protected void openConnection() {
if (logger.isInfoEnabled()) {
logger.info("Connecting to WebSocket at " + getUri()); logger.info("Connecting to WebSocket at " + getUri());
}
ListenableFuture<WebSocketSession> future = ListenableFuture<WebSocketSession> future =
this.client.doHandshake(this.webSocketHandler, this.headers, getUri()); this.client.doHandshake(this.webSocketHandler, this.headers, getUri());
@ -145,8 +146,8 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport {
logger.info("Successfully connected"); logger.info("Successfully connected");
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable ex) {
logger.error("Failed to connect", t); logger.error("Failed to connect", ex);
} }
}); });
} }
@ -158,7 +159,7 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport {
@Override @Override
protected boolean isConnected() { protected boolean isConnected() {
return ((this.webSocketSession != null) && (this.webSocketSession.isOpen())); return (this.webSocketSession != null && this.webSocketSession.isOpen());
} }
} }

Loading…
Cancel
Save