| package com.google.gwt.dev.shell.remoteui; |
| |
| import com.google.gwt.dev.shell.remoteui.MessageTransport.RequestException; |
| import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message; |
| import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Failure; |
| import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Request; |
| import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Response; |
| import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Request.DevModeRequest; |
| import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Request.DevModeRequest.RequestType; |
| import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Response.DevModeResponse; |
| |
| import junit.framework.TestCase; |
| |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| |
| public class MessageTransportTest extends TestCase { |
| |
| private static class MockNetwork { |
| private final Socket clientSocket; |
| private final Socket serverSocket; |
| |
| private final ServerSocket listenSocket; |
| |
| public MockNetwork(Socket clientSocket, Socket serverSocket, |
| ServerSocket listenSocket) { |
| this.clientSocket = clientSocket; |
| this.serverSocket = serverSocket; |
| this.listenSocket = listenSocket; |
| } |
| |
| public Socket getClientSocket() { |
| return clientSocket; |
| } |
| |
| public Socket getServerSocket() { |
| return serverSocket; |
| } |
| |
| public void shutdown() { |
| try { |
| clientSocket.close(); |
| } catch (IOException e) { |
| // Ignore |
| } |
| |
| try { |
| serverSocket.close(); |
| } catch (IOException e) { |
| // Ignore |
| } |
| |
| try { |
| listenSocket.close(); |
| } catch (IOException e) { |
| // Ignore |
| } |
| } |
| } |
| |
| private static MockNetwork createMockNetwork() throws IOException { |
| InetAddress localHost = InetAddress.getLocalHost(); |
| ServerSocket listenSocket = new ServerSocket(0, 1, localHost); |
| Socket clientSocket = new Socket(localHost, listenSocket.getLocalPort()); |
| Socket serverSocket = listenSocket.accept(); |
| return new MockNetwork(clientSocket, serverSocket, listenSocket); |
| } |
| |
| /** |
| * Tests that sending an async request to a server when the server's socket is |
| * closed with result in an ExecutionException on a call to future.get(). |
| * |
| * @throws ExecutionException |
| * @throws InterruptedException |
| * @throws IOException |
| */ |
| public void testExecuteAsyncRequestWithClosedServerSocket() |
| throws IOException, InterruptedException { |
| MockNetwork network = createMockNetwork(); |
| |
| /* |
| * Define a dummy request processor. The message transport is being set up |
| * on the client side, which means that it should not be receiving any |
| * requests (any responses). |
| */ |
| RequestProcessor requestProcessor = new RequestProcessor() { |
| public Response execute(Request request) throws Exception { |
| fail("Should not reach here."); |
| return null; |
| } |
| }; |
| |
| // Set up a transport on the client side |
| MessageTransport messageTransport = new MessageTransport( |
| network.getClientSocket().getInputStream(), |
| network.getClientSocket().getOutputStream(), requestProcessor, |
| new MessageTransport.ErrorCallback() { |
| public void onResponseException(Exception e) { |
| } |
| public void onTermination(Exception e) { |
| } |
| }); |
| messageTransport.start(); |
| |
| Message.Request.Builder requestMessageBuilder = Message.Request.newBuilder(); |
| requestMessageBuilder.setServiceType(Message.Request.ServiceType.DEV_MODE); |
| Message.Request request = requestMessageBuilder.build(); |
| |
| // Close the server's socket; that will close the client's output |
| // stream |
| network.getServerSocket().close(); |
| |
| int sleepCycles = 0; |
| while (!network.getServerSocket().isClosed() && sleepCycles < 8) { |
| // Wait until the stream is closed before attempting to execute the |
| // request. |
| Thread.sleep(250); |
| sleepCycles++; |
| } |
| |
| assertTrue("Unable to close socket; cannot proceed with the test.", |
| network.getServerSocket().isClosed()); |
| |
| Future<Response> responseFuture = null; |
| responseFuture = messageTransport.executeRequestAsync(request); |
| assertNotNull(responseFuture); |
| |
| try { |
| responseFuture.get(2, TimeUnit.SECONDS); |
| fail("Should have thrown an exception"); |
| } catch (TimeoutException te) { |
| fail("Should not have timed out"); |
| } catch (ExecutionException e) { |
| /* |
| * An IOException can happen if the request gets in the queue before the |
| * message processing thread terminates. If the request gets in the queue |
| * after the message processing thread terminates, then the result will be |
| * an IllegalStateException. |
| */ |
| assertTrue("Expected: IllegalStateException or IOException, actual:" |
| + e.getCause(), e.getCause() instanceof IllegalStateException |
| || e.getCause() instanceof IOException); |
| } catch (Exception e) { |
| fail("Should not have thrown any other exception"); |
| } |
| |
| network.shutdown(); |
| } |
| |
| /** |
| * Tests that an async request to a remote server is successfully sent, and |
| * the server's response is successfully received. |
| */ |
| public void testExecuteRequestAsync() throws InterruptedException, |
| ExecutionException, IOException, TimeoutException { |
| |
| MockNetwork network = createMockNetwork(); |
| |
| /* |
| * Define a dummy request processor. The message transport is being set up |
| * on the client side, which means that it should not be receiving any |
| * requests (any responses). |
| */ |
| RequestProcessor requestProcessor = new RequestProcessor() { |
| public Response execute(Request request) throws Exception { |
| fail("Should not reach here."); |
| return null; |
| } |
| }; |
| |
| // Set up a transport on the client side |
| MessageTransport messageTransport = new MessageTransport( |
| network.getClientSocket().getInputStream(), |
| network.getClientSocket().getOutputStream(), requestProcessor, null); |
| messageTransport.start(); |
| |
| // Generate a new request |
| DevModeRequest.Builder devModeRequestBuilder = DevModeRequest.newBuilder(); |
| devModeRequestBuilder.setRequestType(RequestType.CAPABILITY_EXCHANGE); |
| Message.Request.Builder requestMessageBuilder = Message.Request.newBuilder(); |
| requestMessageBuilder.setServiceType(Message.Request.ServiceType.DEV_MODE); |
| requestMessageBuilder.setDevModeRequest(devModeRequestBuilder); |
| Message.Request request = requestMessageBuilder.build(); |
| |
| // Execute the request on the remote server |
| Future<Response> responseFuture = messageTransport.executeRequestAsync(request); |
| assertNotNull(responseFuture); |
| |
| // Get the request on the server side |
| Message receivedRequest = Message.parseDelimitedFrom(network.getServerSocket().getInputStream()); |
| assertEquals(receivedRequest.getRequest(), request); |
| |
| // Generate a response on the server |
| DevModeResponse.CapabilityExchange.Capability.Builder capabilityBuilder = DevModeResponse.CapabilityExchange.Capability.newBuilder(); |
| capabilityBuilder.setCapability(DevModeRequest.RequestType.RESTART_WEB_SERVER); |
| DevModeResponse.CapabilityExchange.Builder capabilityExchangeResponseBuilder = DevModeResponse.CapabilityExchange.newBuilder(); |
| capabilityExchangeResponseBuilder.addCapabilities(capabilityBuilder); |
| DevModeResponse.Builder devModeResponseBuilder = DevModeResponse.newBuilder(); |
| devModeResponseBuilder.setResponseType(DevModeResponse.ResponseType.CAPABILITY_EXCHANGE); |
| devModeResponseBuilder.setCapabilityExchange(capabilityExchangeResponseBuilder); |
| Response.Builder responseBuilder = Response.newBuilder(); |
| responseBuilder.setDevModeResponse(devModeResponseBuilder); |
| Response response = responseBuilder.build(); |
| Message.Builder responseMsgBuilder = Message.newBuilder(); |
| responseMsgBuilder.setMessageType(Message.MessageType.RESPONSE); |
| // Make sure we set the right message id |
| responseMsgBuilder.setMessageId(receivedRequest.getMessageId()); |
| responseMsgBuilder.setResponse(response); |
| Message responseMsg = responseMsgBuilder.build(); |
| |
| // Send the response back to the client |
| responseMsg.writeDelimitedTo(network.getServerSocket().getOutputStream()); |
| |
| // Make sure that the response received on the client is identical to |
| // the response sent by the server |
| assertEquals(responseFuture.get(2, TimeUnit.SECONDS), response); |
| |
| network.shutdown(); |
| } |
| |
| /** |
| * Tests that an async request to a remote server which ends up throwing an |
| * exception on the server side ends up throwing the proper exception via the |
| * future that the client is waiting on. |
| */ |
| public void testExecuteRequestAsyncServerThrowsException() throws IOException { |
| MockNetwork network = createMockNetwork(); |
| |
| /* |
| * Define a dummy request processor. The message transport is being set up |
| * on the client side, which means that it should not be receiving any |
| * requests (any responses). |
| */ |
| RequestProcessor requestProcessor = new RequestProcessor() { |
| public Response execute(Request request) throws Exception { |
| fail("Should not reach here."); |
| return null; |
| } |
| }; |
| |
| // Set up a message transport on the client side |
| MessageTransport messageTransport = new MessageTransport( |
| network.getClientSocket().getInputStream(), |
| network.getClientSocket().getOutputStream(), requestProcessor, |
| new MessageTransport.ErrorCallback() { |
| public void onResponseException(Exception e) { |
| } |
| public void onTermination(Exception e) { |
| } |
| }); |
| messageTransport.start(); |
| |
| // Generate a new request |
| Message.Request.Builder requestMessageBuilder = Message.Request.newBuilder(); |
| requestMessageBuilder.setServiceType(Message.Request.ServiceType.DEV_MODE); |
| Message.Request request = requestMessageBuilder.build(); |
| |
| // Execute the request on the remote server |
| Future<Response> responseFuture = messageTransport.executeRequestAsync(request); |
| assertNotNull(responseFuture); |
| |
| // Get the request on the server side |
| Message receivedRequest = Message.parseDelimitedFrom(network.getServerSocket().getInputStream()); |
| assertEquals(receivedRequest.getRequest(), request); |
| |
| // Generate a failure response on the server |
| Failure.Builder failureBuilder = Failure.newBuilder(); |
| failureBuilder.setMessage("Unable to process the request."); |
| Message.Builder messageBuilder = Message.newBuilder(); |
| // Make sure that we set the matching message id |
| messageBuilder.setMessageId(receivedRequest.getMessageId()); |
| messageBuilder.setMessageType(Message.MessageType.FAILURE); |
| messageBuilder.setFailure(failureBuilder); |
| Message failureMsg = messageBuilder.build(); |
| |
| // Send the failure message back to the client |
| failureMsg.writeDelimitedTo(network.getServerSocket().getOutputStream()); |
| |
| // Wait for the response on the client. This should result in a |
| // RequestException being thrown. |
| try { |
| responseFuture.get(2, TimeUnit.SECONDS); |
| fail("Should have thrown an exception"); |
| } catch (TimeoutException te) { |
| fail("Should not have timed out"); |
| } catch (ExecutionException e) { |
| // This is where we should hit |
| assertTrue("Expected: MessageTransport.RequestException, actual:" |
| + e.getCause(), e.getCause() instanceof RequestException); |
| RequestException re = (RequestException) e.getCause(); |
| assertEquals(re.getMessage(), "Unable to process the request."); |
| } catch (Exception e) { |
| fail("Should not have thrown any other exception"); |
| } |
| |
| network.shutdown(); |
| } |
| |
| /** |
| * Tests that a client request is successfully received by the |
| * RequestProcessor, and the response generated by the RequestProcessor is |
| * successfully received by the client. |
| * |
| * @throws IOException |
| * @throws ExecutionException |
| * @throws InterruptedException |
| */ |
| public void testRequestProcessor() throws IOException { |
| MockNetwork network = createMockNetwork(); |
| |
| // Create the request that will be sent to the server |
| DevModeRequest.Builder devModeRequestBuilder = DevModeRequest.newBuilder(); |
| devModeRequestBuilder.setRequestType(DevModeRequest.RequestType.CAPABILITY_EXCHANGE); |
| Message.Request.Builder clientRequestBuilder = Message.Request.newBuilder(); |
| clientRequestBuilder.setDevModeRequest(devModeRequestBuilder); |
| clientRequestBuilder.setServiceType(Message.Request.ServiceType.DEV_MODE); |
| final Message.Request clientRequest = clientRequestBuilder.build(); |
| |
| // Create the response that will be sent back from the server |
| DevModeResponse.Builder devModeResponseBuilder = DevModeResponse.newBuilder(); |
| devModeResponseBuilder.setResponseType(DevModeResponse.ResponseType.CAPABILITY_EXCHANGE); |
| Message.Response.Builder clientResponseBuilder = Message.Response.newBuilder(); |
| clientResponseBuilder.setDevModeResponse(devModeResponseBuilder); |
| final Message.Response clientResponse = clientResponseBuilder.build(); |
| |
| /* |
| * Define a request processor, which will expect to receive the request that |
| * we've defined, and then return the response that we've defined. |
| */ |
| RequestProcessor requestProcessor = new RequestProcessor() { |
| public Response execute(Request request) throws Exception { |
| assertEquals(clientRequest, request); |
| return clientResponse; |
| } |
| }; |
| |
| // Start up the message transport on the server side |
| MessageTransport messageTransport = new MessageTransport( |
| network.getClientSocket().getInputStream(), |
| network.getClientSocket().getOutputStream(), requestProcessor, |
| new MessageTransport.ErrorCallback() { |
| public void onResponseException(Exception e) { |
| } |
| public void onTermination(Exception e) { |
| } |
| }); |
| messageTransport.start(); |
| |
| // Send the request from the client to the server |
| Message.Builder clientRequestMsgBuilder = Message.newBuilder(); |
| clientRequestMsgBuilder.setMessageType(Message.MessageType.REQUEST); |
| clientRequestMsgBuilder.setMessageId(25); |
| clientRequestMsgBuilder.setRequest(clientRequest); |
| Message clientRequestMsg = clientRequestMsgBuilder.build(); |
| clientRequestMsg.writeDelimitedTo(network.getServerSocket().getOutputStream()); |
| |
| // Receive the response on the client (which was returned by the |
| // RequestProcessor) |
| Message receivedResponseMsg = Message.parseDelimitedFrom(network.getServerSocket().getInputStream()); |
| |
| // Make sure the message ids match |
| assertEquals(receivedResponseMsg.getMessageId(), 25); |
| |
| // Make sure that the response matches the one that was returned by the |
| // RequestProcessor |
| assertEquals(receivedResponseMsg.getResponse(), clientResponse); |
| |
| network.shutdown(); |
| } |
| |
| /** |
| * Tests that a client request is successfully received by the |
| * RequestProcessor, and the exception thrown by the RequestProcessor is |
| * passed back in the form of an error response to the client. |
| * |
| * @throws IOException |
| * @throws ExecutionException |
| * @throws InterruptedException |
| */ |
| public void testRequestProcessorThrowsException() throws IOException { |
| MockNetwork network = createMockNetwork(); |
| |
| /* |
| * Define a request processor that throws an exception when it receives the |
| * request. We'll expect to receive this exception as a failure message on |
| * the client side. |
| */ |
| RequestProcessor requestProcessor = new RequestProcessor() { |
| public Response execute(Request request) throws Exception { |
| throw new Exception("There was an exception processing this request."); |
| } |
| }; |
| |
| // Start up the message transport on the server side |
| MessageTransport messageTransport = new MessageTransport( |
| network.getClientSocket().getInputStream(), |
| network.getClientSocket().getOutputStream(), requestProcessor, |
| new MessageTransport.ErrorCallback() { |
| public void onResponseException(Exception e) { |
| } |
| public void onTermination(Exception e) { |
| } |
| }); |
| messageTransport.start(); |
| |
| // Send a request to the server |
| Message.Request.Builder clientRequestBuilder = Message.Request.newBuilder(); |
| clientRequestBuilder.setServiceType(Message.Request.ServiceType.DEV_MODE); |
| final Message.Request clientRequest = clientRequestBuilder.build(); |
| Message.Builder clientRequestMsgBuilder = Message.newBuilder(); |
| clientRequestMsgBuilder.setMessageType(Message.MessageType.REQUEST); |
| clientRequestMsgBuilder.setMessageId(25); |
| clientRequestMsgBuilder.setRequest(clientRequest); |
| Message clientRequestMsg = clientRequestMsgBuilder.build(); |
| clientRequestMsg.writeDelimitedTo(network.getServerSocket().getOutputStream()); |
| |
| // Receive the response on the client (which was returned by the |
| // RequestProcessor) |
| Message receivedResponseMsg = Message.parseDelimitedFrom(network.getServerSocket().getInputStream()); |
| |
| // Make sure the message ids match |
| assertEquals(receivedResponseMsg.getMessageId(), 25); |
| |
| // Verify that the message is of type FAILURE |
| assertEquals(receivedResponseMsg.getMessageType(), |
| Message.MessageType.FAILURE); |
| |
| // Verify that the failure message field is set |
| assertNotNull(receivedResponseMsg.getFailure()); |
| |
| // Verify that the actual failure message is equal to the message |
| // set for the Exception in the RequestProcessor |
| assertEquals(receivedResponseMsg.getFailure().getMessage(), |
| "There was an exception processing this request."); |
| |
| network.shutdown(); |
| } |
| } |