diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java b/spring-integration-http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java deleted file mode 100644 index 9de09b8ec5..0000000000 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpInboundEndpoint.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright 2002-2010 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 java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.integration.core.Message; -import org.springframework.integration.gateway.SimpleMessagingGateway; -import org.springframework.integration.message.MessageTimeoutException; -import org.springframework.util.Assert; -import org.springframework.web.HttpRequestHandler; -import org.springframework.web.multipart.MultipartResolver; -import org.springframework.web.servlet.DispatcherServlet; -import org.springframework.web.servlet.View; - -/** - * An inbound endpoint for handling an HTTP request and generating a response. - *

- * By default GET and POST requests are accepted, but the 'supportedMethods' - * property may be set to include others or limit the options (e.g. POST only). - * By default the request will be converted to a Message payload according to - * the rules of the {@link DefaultInboundRequestMapper}. - *

- * To customize the mapping of the request to the Message payload, provide - * a reference to an {@link InboundRequestMapper} implementation to the - * {@link #setRequestMapper(InboundRequestMapper)} method. - *

- * The value for {@link #expectReply} is false by default. - * This means that as soon as the Message is created and passed to the - * {@link #setRequestChannel(org.springframework.integration.core.MessageChannel) request channel}, - * a response will be generated. If a {@link #setView(View) view} has been - * provided, it will be invoked to render the response, and it will have - * access to the request message in the model map. The corresponding key - * in that map is determined by the {@link #requestKey} property (with a - * default of "requestMessage"). If no view is provided, and the 'expectReply' - * value is false then a simple OK status response will be issued. - *

- * To handle request-reply scenarios, set the 'expectReply' flag to - * true. By default, the reply Message's payload will be - * extracted prior to generating a response. The payload must be either - * a String, a byte array, or a Serializable object. To have the entire - * serialized Message written as the response body, switch the - * {@link #extractReplyPayload} value to false. - *

- * In the request-reply case, if a 'view' is provided, the response will - * not be generated directly from the reply Message or its extracted payload. - * Instead, the model map will be passed to that view, and it will contain - * either the reply Message or payload depending on the value of - * {@link #extractReplyPayload}. The corresponding key in the map will be - * determined by the {@link #replyKey} property (with a default of "reply"). - * The map will also contain the original request Message as described above. - * - * @author Mark Fisher - * @since 1.0.2 - */ -public class HttpInboundEndpoint extends SimpleMessagingGateway implements HttpRequestHandler { - - private static final String DEFAULT_REQUEST_KEY = "requestMessage"; - - private static final String DEFAULT_REPLY_KEY = "reply"; - - - private volatile List supportedMethods = Arrays.asList("GET", "POST"); - - private volatile boolean expectReply; - - private volatile InboundRequestMapper requestMapper; - - private volatile boolean extractReplyPayload = true; - - private volatile View view; - - private volatile String requestKey = DEFAULT_REQUEST_KEY; - - private volatile String replyKey = DEFAULT_REPLY_KEY; - - - /** - * Specify the supported request methods for this endpoint. - * By default, only GET and POST are supported. - */ - public void setSupportedMethods(String... supportedMethods) { - Assert.notEmpty(supportedMethods, "at least one supported method is required"); - for (int i = 0; i < supportedMethods.length; i++) { - supportedMethods[i] = supportedMethods[i].trim().toUpperCase(); - } - this.supportedMethods = Arrays.asList(supportedMethods); - } - - /** - * Specify whether this endpoint should perform a request/reply - * operation. Otherwise, it will only send the message and - * immediately generate a response. The default is 'false'. - */ - public void setExpectReply(boolean expectReply) { - this.expectReply = expectReply; - } - - /** - * Specify an {@link InboundRequestMapper} implementation to map from the - * inbound {@link HttpServletRequest} instances to Messages at runtime. - * The default implementation is {@link DefaultInboundRequestMapper}. - */ - public void setRequestMapper(InboundRequestMapper requestMapper) { - Assert.notNull(requestMapper, "requestMapper must not be null"); - this.requestMapper = requestMapper; - } - - /** - * Specify whether the reply Message's payload should be passed in - * the response. If this is set to 'false', the entire Message will - * be sent as bytes. Otherwise, the reply Message payload must be - * a String or byte array. If a 'view' has been provided, - * the reply value will be sent in the model Map to that View. - * If the 'view' is null, the String or byte array - * will be written directly to the HTTP response. - *

The default value is 'true'. - * @see #setView(View) - */ - public void setExtractReplyPayload(boolean extractReplyPayload) { - this.extractReplyPayload = extractReplyPayload; - } - - /** - * Specify a {@link View} to be used for rendering the - * response. If no View is provided, the reply Message or its - * payload will be written directly to the response. - * @see #setExtractReplyPayload(boolean) - */ - public void setView(View view) { - this.view = view; - } - - /** - * Specify the key to be used when storing the request Message in the model - * map. This is only necessary when a {@link #setView(View) view} has been - * provided for rendering the response. The default key is "requestMessage". - */ - public void setRequestKey(String requestKey) { - this.requestKey = (requestKey != null) ? requestKey : DEFAULT_REQUEST_KEY; - } - - /** - * Specify the key to be used when storing the reply Message or payload in - * the model map. This is only necessary when a {@link #setView(View) view} - * has been provided for rendering the response. The default key is "reply". - */ - public void setReplyKey(String replyKey) { - this.replyKey = (replyKey != null) ? replyKey : DEFAULT_REPLY_KEY; - } - - @Override - protected void onInit() throws Exception { - if (this.requestMapper == null) { - this.configureDefaultRequestMapper(); - } - super.onInit(); - } - - private void configureDefaultRequestMapper() { - DefaultInboundRequestMapper defaultMapper = new DefaultInboundRequestMapper(); - if (this.getBeanFactory() != null) { - try { - MultipartResolver multipartResolver = - this.getBeanFactory().getBean(DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); - if (logger.isDebugEnabled()) { - logger.debug("Using MultipartResolver [" + multipartResolver + "]"); - } - defaultMapper.setMultipartResolver(multipartResolver); - } - catch (NoSuchBeanDefinitionException e) { - if (logger.isDebugEnabled()) { - logger.debug("Unable to locate MultipartResolver with name '" + DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME + - "': no multipart request handling will be supported."); - } - } - } - this.requestMapper = defaultMapper; - } - - public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - Assert.notNull(this.requestMapper, "HttpInboundEndpoint has not been initialized."); - if (!this.supportedMethods.contains(request.getMethod())) { - response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); - return; - } - try { - Message requestMessage = this.requestMapper.toMessage(request); - Object reply = this.handleRequestMessage(requestMessage); - this.generateResponse(requestMessage, reply, request, response); - } - catch (ResponseStatusCodeException e) { - response.setStatus(e.getStatusCode()); - } - catch (ServletException e) { - throw e; - } - catch (IOException e) { - throw e; - } - catch (Exception e) { - throw new ServletException(e); - } - } - - private Object handleRequestMessage(Message requestMessage) { - Object reply = null; - if (this.expectReply) { - if (this.extractReplyPayload) { - reply = this.sendAndReceive(requestMessage); - } - else { - reply = this.sendAndReceiveMessage(requestMessage); - } - if (reply == null) { - throw new MessageTimeoutException(requestMessage, - "failed to handle Message within specified timeout value"); - } - } - else { - this.send(requestMessage); - } - return reply; - } - - private void generateResponse(Message requestMessage, Object reply, - HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException, IOException { - if (this.view != null) { - Map model = new HashMap(); - model.put(this.requestKey, requestMessage); - if (reply != null) { - model.put(this.replyKey, reply); - } - try { - this.view.render(model, httpRequest, httpResponse); - } - catch (Exception e) { - throw new ServletException("failed to render view", e); - } - } - else if (reply == null) { - httpResponse.setStatus(HttpServletResponse.SC_OK); - } - else if (reply instanceof String) { - httpResponse.setContentType("text/plain"); - httpResponse.setContentLength(((String) reply).length()); - httpResponse.getWriter().print((String) reply); - httpResponse.flushBuffer(); - } - else if (reply instanceof byte[]) { - byte[] bytes = (byte[]) reply; - httpResponse.setContentType("application/octet-stream"); - httpResponse.setContentLength(bytes.length); - httpResponse.getOutputStream().write(bytes); - httpResponse.flushBuffer(); - } - else if (reply instanceof Serializable) { - // either a Serializable payload or the Message itself - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - ObjectOutputStream objectStream = new ObjectOutputStream(byteStream); - objectStream.writeObject(reply); - objectStream.flush(); - objectStream.close(); - byte[] bytes = byteStream.toByteArray(); - httpResponse.getOutputStream().write(bytes); - httpResponse.setContentType("application/x-java-serialized-object"); - httpResponse.setContentLength(bytes.length); - httpResponse.flushBuffer(); - } - else { - throw new ServletException("failed to generate HTTP response from reply Message"); - } - } - -} diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java deleted file mode 100644 index 422ab4fd0c..0000000000 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpInboundEndpointTests.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright 2002-2010 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.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -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.integration.message.StringMessage; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.util.MultiValueMap; -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); - endpoint.afterPropertiesSet(); - 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((List) msgParams.get(key), - is(Collections.singletonList(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_withCustomRequestMapper_requestObjectIsInPayload() - throws ServletException, IOException { - endpoint.setRequestMapper(new InboundRequestMapper() { - public Message toMessage(HttpServletRequest request) throws Exception { - return new StringMessage(request.getRequestURI()); - } - }); - expect(requestChannel.send(isA(Message.class))).andAnswer( - new IAnswer() { - @SuppressWarnings("unchecked") - public Boolean answer() throws Throwable { - assertThat(((Message) getCurrentArguments()[0]).getPayload(), is((Object) "/anyurl")); - 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_withPOSTRequestAndFormContent_sameInMessagePayload() - throws ServletException, IOException { - addRequestContent("POST", "application/x-www-form-urlencoded", 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 { - MultiValueMap payloadMap = (MultiValueMap) - ((Message) getCurrentArguments()[0]).getPayload(); - for (String key : sourceParams.keySet()) { - assertThat(payloadMap.get(key), - is(Collections.singletonList(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 - @SuppressWarnings("unchecked") - 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)); - endpoint.afterPropertiesSet(); - } - catch (Exception e) { - throw new IllegalStateException(e); - } - expect(endpoint.sendAndReceive(anyObject())).andReturn(anyPayload); - endpoint.setExpectReply(true); - replay(endpoint); - } - -}