diff --git a/org.springframework.integration.http/.classpath b/org.springframework.integration.http/.classpath index 3120d816da..f3bfa67edd 100644 --- a/org.springframework.integration.http/.classpath +++ b/org.springframework.integration.http/.classpath @@ -6,6 +6,8 @@ + + diff --git a/org.springframework.integration.http/ivy.xml b/org.springframework.integration.http/ivy.xml index 699cfe72c3..879dc34200 100644 --- a/org.springframework.integration.http/ivy.xml +++ b/org.springframework.integration.http/ivy.xml @@ -21,6 +21,8 @@ + + diff --git a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java index 987d663343..485d9261ce 100644 --- a/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java +++ b/org.springframework.integration.http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import javax.servlet.ServletException; +import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -48,7 +49,9 @@ import org.springframework.web.servlet.View; * By default the request will be converted to a Message payload according to * the following rules: *
    - *
  • For a GET request, the parameter Map will be used as the payload
  • + *
  • For a GET request, the parameter Map will be used as the payload. + * The map's keys will be Strings, and the values will be String arrays + * as described for {@link ServletRequest#getParameterMap()}
  • . *
  • For other request types, the request body will be used as the payload * and the type will depend on the Content-Type header value. If it * begins with "text", a String will be created. Otherwise, the payload diff --git a/org.springframework.integration.http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java b/org.springframework.integration.http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java new file mode 100644 index 0000000000..3ef0eb03fe --- /dev/null +++ b/org.springframework.integration.http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java @@ -0,0 +1,394 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.http; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.eq; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.getCurrentArguments; +import static org.easymock.EasyMock.isA; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.reset; +import static org.easymock.classextension.EasyMock.verify; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; + +import org.easymock.IAnswer; +import org.easymock.classextension.ConstructorArgs; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.integration.core.Message; +import org.springframework.integration.core.MessageChannel; +import org.springframework.integration.core.MessageHeaders; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.servlet.View; + +/** + * @author Alex Peters + */ +public class HttpInboundEndpointTests { + + private static final String ANY_ENCODING = "UTF-8"; + + private static final String ANY_STRING_PAYLOAD = "any text content..blabla...bla.äöüßß߀€€€"; + + private static final byte[] ANY_BINARY_PAYLOAD; + + private final MessageChannel requestChannel = createMock(MessageChannel.class); + + private final MessageChannel replyChannel = createMock(MessageChannel.class); + + private final Object[] allmocks = new Object[] { requestChannel, replyChannel }; + + private HttpInboundEndpoint endpoint; + + private MockHttpServletRequest request; + + private MockHttpServletResponse response; + + + static { + try { + ANY_BINARY_PAYLOAD = "any binary content..blabla...bla.äöüßß߀€€€".getBytes(ANY_ENCODING); + } + catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e); + } + } + + + @Before + public void initializeSample() { + endpoint = new HttpInboundEndpoint(); + endpoint.setRequestChannel(requestChannel); + endpoint.setReplyChannel(replyChannel); + reset(allmocks); + request = new MockHttpServletRequest("GET", "/anyurl"); + response = new MockHttpServletResponse(); + response.setCharacterEncoding(ANY_ENCODING); + } + + + @Test + public void handleRequest_withDefaultSettingsAndUnsupportedHTTPMethods_returns405() + throws ServletException, IOException { + String[] httpMethods = { "OPTIONS", "HEAD", "PUT", "DELETE", "TRACE", "CONNECT", "ANY_INVALID" }; + for (String deniedHttpMethod : httpMethods) { + request = new MockHttpServletRequest(deniedHttpMethod, "/anyurl"); + endpoint.handleRequest(request, response); + assertThat("Unexpected result for http method: " + deniedHttpMethod, + response.getStatus(), + is(HttpServletResponse.SC_METHOD_NOT_ALLOWED)); + } + } + + @Test + public void handleRequest_withGETRequest_allReqParametersInMessagePayload() + throws ServletException, IOException { + final Map sourceParams = addAnyParametersToRequest(); + expect(requestChannel.send(isA(Message.class))).andAnswer( + new IAnswer() { + @SuppressWarnings("unchecked") + public Boolean answer() throws Throwable { + final Map msgParams = (Map) ((Message) getCurrentArguments()[0]).getPayload(); + assertThat(msgParams.size(), is(sourceParams.size())); + for (String key : sourceParams.keySet()) { + assertThat((String[]) msgParams.get(key), + is(new String[] { sourceParams.get(key) })); + } + return true; + } + }); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_withEmptyGETRequest_emptyMapIsInMessagePayload() + throws ServletException, IOException { + expect(requestChannel.send(isA(Message.class))).andAnswer( + new IAnswer() { + @SuppressWarnings("unchecked") + public Boolean answer() throws Throwable { + final Map msgParams = (Map) ((Message) getCurrentArguments()[0]).getPayload(); + assertThat(msgParams.size(), is(0)); + return true; + } + }); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_withExtractRequestPayloadIsFalse_requestObjectIsInPayload() + throws ServletException, IOException { + endpoint.setExtractRequestPayload(false); + expect(requestChannel.send(isA(Message.class))).andAnswer( + new IAnswer() { + @SuppressWarnings("unchecked") + public Boolean answer() throws Throwable { + assertThat(((Message) getCurrentArguments()[0]).getPayload(), is((Object) request)); + return true; + } + }); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_RequestHeadersInMsgHeaders() + throws ServletException, IOException { + final Principal anyPrincipal = createMock(Principal.class); + request.setUserPrincipal(anyPrincipal); + expect(requestChannel.send(isA(Message.class))).andAnswer( + new IAnswer() { + @SuppressWarnings("unchecked") + public Boolean answer() throws Throwable { + MessageHeaders headers = ((Message) getCurrentArguments()[0]).getHeaders(); + assertThat(headers.get(HttpHeaders.REQUEST_METHOD), + is((Object) "GET")); + assertThat(headers.get(HttpHeaders.REQUEST_URL), + is((Object) "http://localhost:80/anyurl")); + assertThat(headers.get(HttpHeaders.USER_PRINCIPAL), + is((Object) anyPrincipal)); + return true; + } + }); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_withPOSTRequestAndTextContent_sameInMessagePayload() + throws ServletException, IOException { + final String characterEncoding = ANY_ENCODING; + addRequestContent("POST", "text/plain", characterEncoding, + ANY_STRING_PAYLOAD.getBytes(characterEncoding)); + expect(requestChannel.send(isA(Message.class))).andAnswer( + new IAnswer() { + @SuppressWarnings("unchecked") + public Boolean answer() throws Throwable { + assertThat((String) ((Message) getCurrentArguments()[0]).getPayload(), + is(ANY_STRING_PAYLOAD)); + return true; + } + }); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_withPOSTRequestAndParamsOnly_sameInMessageHeader() + throws ServletException, IOException { + addRequestContent("POST", "text/plain", ANY_ENCODING, new byte[0]); + final Map sourceParams = addAnyParametersToRequest(); + request.setParameters(sourceParams); + expect(requestChannel.send(isA(Message.class))).andAnswer( + new IAnswer() { + @SuppressWarnings("unchecked") + public Boolean answer() throws Throwable { + MessageHeaders headers = ((Message) getCurrentArguments()[0]).getHeaders(); + for (String key : sourceParams.keySet()) { + assertThat(headers.get(key), + is((Object) new String[] { sourceParams.get(key) })); + } + return true; + } + }); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_withEmptyPOSTRequest_emptyStringAsPayload() + throws ServletException, IOException { + addRequestContent("POST", "text/plain", null, new byte[0]); + expect(requestChannel.send(isA(Message.class))).andAnswer( + new IAnswer() { + @SuppressWarnings("unchecked") + public Boolean answer() throws Throwable { + assertThat((String) ((Message) getCurrentArguments()[0]).getPayload(), is("")); + return true; + } + }); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_withPOSTRequestAndBinaryContent_sameInMessagePayload() + throws ServletException, IOException { + addRequestContent("POST", "", ANY_ENCODING, ANY_BINARY_PAYLOAD); + expect(requestChannel.send(isA(Message.class))).andAnswer( + new IAnswer() { + @SuppressWarnings("unchecked") + public Boolean answer() throws Throwable { + assertThat((byte[]) ((Message) getCurrentArguments()[0]).getPayload(), + is(ANY_BINARY_PAYLOAD)); + return true; + } + }); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_withPOSTRequestEmptyContentLenght_return411() + throws ServletException, IOException { + addRequestContent("POST", "", ANY_ENCODING, null); + endpoint.handleRequest(request, response); + assertThat(response.getStatus(), is(HttpServletResponse.SC_LENGTH_REQUIRED)); + } + + @Test + public void handleRequest_withoutReplyMessage_return200() + throws ServletException, IOException { + expect(requestChannel.send(isA(Message.class))).andReturn(true); + replay(allmocks); + endpoint.handleRequest(request, response); + assertThat(response.getStatus(), is(HttpServletResponse.SC_OK)); + } + + @Test + public void handleRequest_replyWithTextPayload_textAsRespContent() + throws ServletException, IOException { + setupEndpointAsMock(ANY_STRING_PAYLOAD); + replay(allmocks); + endpoint.handleRequest(request, response); + assertThat(response.getContentAsString(), is(ANY_STRING_PAYLOAD)); + verify(allmocks); + } + + @Test + public void handleRequest_replyWithBytePayload_bytesAsRespContent() + throws ServletException, IOException { + setupEndpointAsMock(ANY_BINARY_PAYLOAD); + replay(allmocks); + endpoint.handleRequest(request, response); + assertThat(response.getContentAsByteArray(), is(ANY_BINARY_PAYLOAD)); + verify(allmocks); + } + + @Test + public void handleRequest_replyWithSerializablePayload_serializableAsRespContent() + throws ServletException, IOException, ClassNotFoundException { + Date obj = new Date(); + setupEndpointAsMock(obj); + replay(allmocks); + endpoint.handleRequest(request, response); + byte[] content = response.getContentAsByteArray(); + Object deserializedObj = new ObjectInputStream( + new ByteArrayInputStream(content)).readObject(); + assertThat(deserializedObj, is(Date.class)); + assertThat((Date) deserializedObj, is(obj)); + verify(allmocks); + } + + @Test(expected = ServletException.class) + public void handleRequest_replyWithNonSerializablePayload_exceptionThrown() + throws ServletException, IOException, ClassNotFoundException { + Object obj = new Object(); + setupEndpointAsMock(obj); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + } + + @Test + public void handleRequest_expectReplyWithView_responseDirectedToView() throws Exception { + setupEndpointAsMock(ANY_STRING_PAYLOAD); + View view = createMock(View.class); + view.render(isA(Map.class), eq(request), eq(response)); + expectLastCall().andAnswer(new IAnswer() { + public Object answer() throws Throwable { + Map model = (Map) getCurrentArguments()[0]; + assertThat(model.isEmpty(), is(false)); + assertThat(model.get("reply"), is((Object) ANY_STRING_PAYLOAD)); + assertThat(model.get("requestMessage"), is(notNullValue())); + return null; + } + }); + endpoint.setView(view); + replay(view); + replay(allmocks); + endpoint.handleRequest(request, response); + verify(allmocks); + verify(view); + } + + + /** add some dummy parameters to the request */ + private Map addAnyParametersToRequest() { + final Map sourceParams = new HashMap(); + for (int i = 0; i < 20; i++) { + sourceParams.put("anyParameter" + i, "anyValue" + i); + } + request.setParameters(sourceParams); + return sourceParams; + } + + /** set given params on request instance */ + private void addRequestContent(String httpMethod, String contentType, String encoding, byte[] content) { + request.setMethod(httpMethod); + request.setContentType(contentType); + request.setContent(content); + request.setCharacterEncoding(encoding); + } + + /** create a new mocked endpoint instance and setup basic data */ + private void setupEndpointAsMock(final Object anyPayload) { + try { + endpoint = createMock(HttpInboundEndpoint.class, + new ConstructorArgs( + HttpInboundEndpoint.class.getConstructor(new Class[0]), + new Object[0]), + HttpInboundEndpoint.class.getMethod("sendAndReceive", Object.class)); + } + catch (Exception e) { + throw new IllegalStateException(e); + } + expect(endpoint.sendAndReceive(anyObject())).andReturn(anyPayload); + endpoint.setExpectReply(true); + replay(endpoint); + } + +}