renamed modules org.springframework.integration.* -> spring-integration-*

@Ignore'd SimpleTcpNetOutboundGatewayTests#testOutboundClose() to avoid failure; this failure is correlated to the module name change, but hard to understand how it would be caused by it
This commit is contained in:
Chris Beams
2010-05-25 13:21:25 +00:00
parent b97b2fb090
commit c08a7a657e
1484 changed files with 18 additions and 23 deletions

View File

@@ -0,0 +1,132 @@
/*
* 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 java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Base class for {@link HttpRequestExecutor} implementations.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 1.0.2
*/
public abstract class AbstractHttpRequestExecutor implements HttpRequestExecutor {
protected static final String HTTP_HEADER_ACCEPT_LANGUAGE = "Accept-Language";
protected static final String HTTP_HEADER_ACCEPT_ENCODING = "Accept-Encoding";
protected static final String HTTP_HEADER_CONTENT_ENCODING = "Content-Encoding";
protected static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type";
protected static final String HTTP_HEADER_CONTENT_LENGTH = "Content-Length";
protected static final String ENCODING_GZIP = "gzip";
protected final Log logger = LogFactory.getLog(getClass());
private boolean acceptGzipEncoding = true;
/**
* Set whether to accept GZIP encoding, that is, whether to
* send the HTTP "Accept-Encoding" header with "gzip" as value.
* <p>Default is "true". Turn this flag off if you do not want
* GZIP response compression even if enabled on the HTTP server.
*/
public void setAcceptGzipEncoding(boolean acceptGzipEncoding) {
this.acceptGzipEncoding = acceptGzipEncoding;
}
/**
* Return whether to accept GZIP encoding, that is, whether to
* send the HTTP "Accept-Encoding" header with "gzip" as its value.
*/
public boolean isAcceptGzipEncoding() {
return this.acceptGzipEncoding;
}
/**
* Execute a request to send its content to its target URL.
* @param request the request to execute
* @return the HttpResponse result
* @throws IOException if thrown by I/O operations
* @throws Exception in case of general errors
*/
public final HttpResponse executeRequest(HttpRequest request) throws Exception {
if (logger.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Sending HTTP request to [" + request.getTargetUrl() + "]");
Integer contentLength = request.getContentLength();
if (contentLength != null) {
sb.append(", with size " + contentLength);
}
logger.debug(sb.toString());
}
return doExecuteRequest(request);
}
/**
* Subclasses must implement this method to execute the request.
*/
protected abstract HttpResponse doExecuteRequest(HttpRequest request) throws Exception;
/**
* Default implementation of {@link HttpResponse}.
*/
class DefaultHttpResponse implements HttpResponse {
private final InputStream body;
private final Map<String, List<String>> headers;
public DefaultHttpResponse(InputStream body, Map<String, List<String>> headers) {
this.body = body;
this.headers = (headers != null) ? headers : Collections.<String, List<String>>emptyMap();
}
public InputStream getBody() {
return this.body;
}
public String getFirstHeader(String key) {
List<String> values = this.headers.get(key);
return (values != null && values.size() > 0) ? values.get(0) : null;
}
public Map<String, List<String>> getHeaders() {
return this.headers;
}
public List<String> getHeaders(String key) {
return this.headers.get(key);
}
}
}

View File

@@ -0,0 +1,293 @@
/*
* 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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.OptionsMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.TraceMethod;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Implementation of {@link HttpRequestExecutor} that uses a commons-http
* {@link HttpClient} to execute {@link HttpRequest} instances.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 1.0.2
*/
public class CommonsHttpRequestExecutor extends AbstractHttpRequestExecutor {
/**
* Default timeout value if no HttpClient is explicitly provided.
*/
private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000);
private HttpClient httpClient;
/**
* Create a new CommonsHttpRequestExecutor with a default HttpClient that
* uses a default MultiThreadedHttpConnectionManager.
* Sets the socket read timeout to {@link #DEFAULT_READ_TIMEOUT_MILLISECONDS}.
* @see org.apache.commons.httpclient.HttpClient
* @see org.apache.commons.httpclient.MultiThreadedHttpConnectionManager
*/
public CommonsHttpRequestExecutor() {
this.httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
this.setReadTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS);
}
/**
* Create a new CommonsHttpRequestExecutor with the given HttpClient
* instance. The socket read timeout of the provided HttpClient will not be
* changed.
* @param httpClient the HttpClient instance to use for this request executor
*/
public CommonsHttpRequestExecutor(HttpClient httpClient) {
this.setHttpClient(httpClient);
}
/**
* Set the HttpClient instance to use for this request executor.
*/
public void setHttpClient(HttpClient httpClient) {
Assert.notNull(httpClient, "httpClient must not be null");
this.httpClient = httpClient;
}
/**
* Return the HttpClient instance that this request executor uses.
*/
public HttpClient getHttpClient() {
return this.httpClient;
}
/**
* Set the socket read timeout for the underlying HttpClient. A value of 0
* means <emphasis>never</emphasis> timeout.
* @param timeout the timeout value in milliseconds
* @see org.apache.commons.httpclient.params.HttpConnectionManagerParams#setSoTimeout(int)
* @see #DEFAULT_READ_TIMEOUT_MILLISECONDS
*/
public void setReadTimeout(int timeout) {
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be a non-negative value");
}
this.httpClient.getHttpConnectionManager().getParams().setSoTimeout(timeout);
}
@Override
protected HttpResponse doExecuteRequest(HttpRequest request) throws Exception {
HttpMethod httpMethod = createHttpMethod(request);
try {
if (httpMethod instanceof EntityEnclosingMethod) {
setRequestBody((EntityEnclosingMethod) httpMethod, request.getBody(), request.getContentType());
}
executeHttpMethod(getHttpClient(), httpMethod);
validateResponse(httpMethod);
return new DefaultHttpResponse(readResponseBody(httpMethod), getResponseHeaders(httpMethod));
}
finally {
// Need to explicitly release because it might be pooled.
httpMethod.releaseConnection();
}
}
/**
* Create a HttpMethod for the given {@link HttpRequest}.
* <p>This implementation creates an HttpMethod with the request's target
* URL as well as the "Accept-Language" and "Accept-Encoding" headers. If
* the method is "POST" or "PUT", the "Content-Type" header will also be
* set as specified in the given request.
* @param request the HTTP request to create a method for
* @return the HttpMethod instance
*/
private HttpMethod createHttpMethod(HttpRequest request) {
String url = request.getTargetUrl().toString();
String methodName = request.getRequestMethod();
HttpMethod httpMethod = null;
if ("GET".equals(methodName)) {
httpMethod = new GetMethod(url);
}
else if ("POST".equals(methodName)) {
httpMethod = new PostMethod(url);
}
else if ("PUT".equals(methodName)) {
httpMethod = new PutMethod(url);
}
else if ("DELETE".equals(methodName)) {
httpMethod = new DeleteMethod(url);
}
else if ("TRACE".equals(methodName)) {
httpMethod = new TraceMethod(url);
}
else if ("HEAD".equals(methodName)) {
httpMethod = new HeadMethod(url);
}
else if ("OPTIONS".equals(methodName)) {
httpMethod = new OptionsMethod(url);
}
else {
throw new UnsupportedOperationException("unsupported request method '" + methodName + "'");
}
LocaleContext locale = LocaleContextHolder.getLocaleContext();
if (locale != null) {
httpMethod.addRequestHeader(HTTP_HEADER_ACCEPT_LANGUAGE, StringUtils.toLanguageTag(locale.getLocale()));
}
if (isAcceptGzipEncoding()) {
httpMethod.addRequestHeader(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
}
if (httpMethod instanceof EntityEnclosingMethod) {
String contentType = request.getContentType();
if (contentType != null) {
httpMethod.addRequestHeader(HTTP_HEADER_CONTENT_TYPE, contentType);
}
}
return httpMethod;
}
/**
* Set the given byte stream as the request body.
* <p>This implementation simply sets the byte stream as the
* EntityEnclosingMethod's request body. This can be overridden, for
* example, to write a specific encoding and potentially set appropriate
* HTTP request headers.
* @param httpMethod the EntityEnclosingMethod on which to set the request body
* @param baos the ByteArrayOutputStream that contains the content
* @param contentType the request body's content type
* @throws IOException if thrown by I/O methods
* @see org.apache.commons.httpclient.methods.PostMethod#setRequestBody(java.io.InputStream)
* @see org.apache.commons.httpclient.methods.PostMethod#setRequestEntity
* @see org.apache.commons.httpclient.methods.InputStreamRequestEntity
*/
private void setRequestBody(
EntityEnclosingMethod httpMethod, ByteArrayOutputStream baos, String contentType)
throws IOException {
httpMethod.setRequestEntity(new ByteArrayRequestEntity(baos.toByteArray(), contentType));
}
/**
* Execute the given HttpMethod instance.
* @param httpClient the HttpClient responsible for execution
* @param httpMethod the HttpMethod to be executed
* @throws IOException if thrown by I/O methods
* @see org.apache.commons.httpclient.HttpClient#executeMethod(org.apache.commons.httpclient.HttpMethod)
*/
private void executeHttpMethod(HttpClient httpClient, HttpMethod httpMethod) throws IOException {
httpClient.executeMethod(httpMethod);
}
/**
* Validate the given response as contained in the HttpMethod object,
* throwing an exception if it does not correspond to a successful HTTP response.
* <p>This implementation rejects any HTTP status code beyond 2xx, to avoid
* parsing the response body and trying to read from a corrupted stream.
* @param httpMethod the executed HttpMethod to validate
* @throws IOException if validation failed
* @see org.apache.commons.httpclient.methods.PostMethod#getStatusCode()
* @see org.apache.commons.httpclient.HttpException
*/
private void validateResponse(HttpMethod httpMethod) throws IOException {
if (httpMethod.getStatusCode() >= 300) {
throw new HttpException(
"Did not receive successful HTTP response from [" + httpMethod.getURI() +
"]: status code = " + httpMethod.getStatusCode() +
", status message = [" + httpMethod.getStatusText() + "]");
}
}
/**
* Extract the response body from the given executed request.
* <p>This implementation simply fetches the HttpMethod's response
* body stream. If the response is recognized as a GZIP response, the
* InputStream will be wrapped in a GZIPInputStream.
* @param httpMethod the HttpMethod from which to read the response body
* @return an InputStream for the response body, or <code>null</code> if no response stream is available
* @throws IOException if thrown by I/O methods
* @see #isGzipResponse
* @see java.util.zip.GZIPInputStream
* @see org.apache.commons.httpclient.HttpMethod#getResponseBodyAsStream()
*/
private InputStream readResponseBody(HttpMethod httpMethod) throws IOException {
byte[] responseBody = httpMethod.getResponseBody();
InputStream responseStream = null;
if (responseBody != null) {
responseStream = new ByteArrayInputStream(responseBody);
if (isGzipResponse(httpMethod)) {
responseStream = new GZIPInputStream(responseStream);
}
}
return responseStream;
}
private Map<String, List<String>> getResponseHeaders(HttpMethod httpMethod) {
Map<String, List<String>> headers = new HashMap<String, List<String>>();
for (Header header : httpMethod.getResponseHeaders()) {
String name = header.getName();
String value = header.getValue();
List<String> values = headers.get(name);
if (values == null) {
values = new ArrayList<String>();
}
values.add(value);
headers.put(name, values);
}
return Collections.unmodifiableMap(headers);
}
/**
* Determine whether the given response indicates a GZIP response.
* <p>This implementation checks whether the HTTP "Content-Encoding"
* header contains "gzip" (in any casing).
* @param httpMethod the HttpMethod to check
* @return whether the given response indicates a GZIP response
* @see org.apache.commons.httpclient.HttpMethod#getResponseHeader(String)
*/
private boolean isGzipResponse(HttpMethod httpMethod) {
Header encodingHeader = httpMethod.getResponseHeader(HTTP_HEADER_CONTENT_ENCODING);
return (encodingHeader != null && encodingHeader.getValue() != null
&& encodingHeader.getValue().toLowerCase().indexOf(ENCODING_GZIP) != -1);
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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 java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.util.Assert;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.servlet.handler.DispatcherServletWebRequest;
/**
* InboundRequestMapper implementation that binds the request parameter map to
* a target instance. The target instance may be a non-singleton bean as
* specified by the {@link #setTargetBeanName(String) 'targetBeanName'}
* property. Otherwise, this mapper's target type must provide a default,
* no-arg constructor.
*
* @author Mark Fisher
* @since 1.0.2
*/
public class DataBindingInboundRequestMapper implements InboundRequestMapper, BeanFactoryAware, InitializingBean {
private volatile Class<?> targetType = Object.class;
private volatile String targetBeanName;
private volatile WebBindingInitializer webBindingInitializer;
private volatile BeanFactory beanFactory;
private volatile boolean validated;
public DataBindingInboundRequestMapper() {
this.targetType = Object.class;
}
public DataBindingInboundRequestMapper(Class<?> targetType) {
Assert.notNull(targetType, "targetType must not be null");
this.targetType = targetType;
}
public void setTargetType(Class<?> targetType) {
this.targetType = targetType;
}
/**
* Specify the name of a bean definition to use when creating the target
* instance. The bean must <em>not</em> be a singleton, and it must be
* compatible with the {@link #targetType}.
* <p>If no 'targetBeanName' value is provided, the target type must
* provide a default, no-arg constructor.
*/
public void setTargetBeanName(String targetBeanName) {
this.targetBeanName = targetBeanName;
}
/**
* Specify an optional {@link WebBindingInitializer} to be invoked prior
* to the request binding process.
*/
public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
this.webBindingInitializer = webBindingInitializer;
}
/**
* Provides the {@link BeanFactory} necessary to look up a
* {@link #setTargetBeanName(String) 'targetBeanName'} if specified.
* This method is typically invoked automatically by the container.
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public final void afterPropertiesSet() {
if (this.targetBeanName == null && Object.class.equals(this.targetType)) {
throw new IllegalArgumentException(
"When no 'targetBeanName' is provided, the 'targetType' must be more specific than Object.");
}
this.validateTargetBeanIfNecessary();
}
private void validateTargetBeanIfNecessary() {
if (this.targetBeanName != null && !this.validated) {
Assert.notNull(this.beanFactory, "beanFactory is required for binding to a bean");
if (this.beanFactory.isSingleton(this.targetBeanName)) {
throw new IllegalArgumentException("binding target bean must not be a singleton");
}
Class<?> beanType = this.beanFactory.getType(this.targetBeanName);
if (beanType != null) {
Assert.isAssignable(this.targetType, beanType);
}
this.validated = true;
}
}
@SuppressWarnings("unchecked")
public Message<?> toMessage(HttpServletRequest request) throws Exception {
ServletRequestDataBinder binder = new ServletRequestDataBinder(getTarget());
this.initBinder(binder, request);
binder.bind(request);
// this will immediately throw any bind Exceptions
Map map = binder.close();
Object payload = map.get(ServletRequestDataBinder.DEFAULT_OBJECT_NAME);
return MessageBuilder.withPayload(payload).build();
}
private void initBinder(ServletRequestDataBinder binder, HttpServletRequest request) {
if (this.webBindingInitializer != null) {
this.webBindingInitializer.initBinder(binder, new DispatcherServletWebRequest(request));
}
}
private Object getTarget() throws InstantiationException, IllegalAccessException {
if (this.targetBeanName != null) {
this.validateTargetBeanIfNecessary();
return this.beanFactory.getBean(this.targetBeanName, this.targetType);
}
return this.targetType.newInstance();
}
}

View File

@@ -0,0 +1,354 @@
/*
* 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.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
/**
* Default implementation of {@link InboundRequestMapper} for inbound HttpServletRequests.
* The request will be mapped according to the following rules:
* <ul>
* <li>For a GET request or a POST request with a Content-Type of
* "application/x-www-form-urlencoded", the parameter Map will be copied as the
* payload. The map will be an instance of {@link MultiValueMap} where the keys are
* Strings and the values are Lists of Strings. Those Lists are populated from the
* String array values of the original request parameter Map as described for the
* {@link ServletRequest#getParameterMap()} method.</li>
* <li>If a MultipartResolver has been provided, and a multipart request is
* detected, the multipart file content will be converted to String for any
* "text" content type, or byte arrays otherwise.</li>
* <li>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. If the Content-Type is
* "application/x-java-serialized-object", the request body will be expected to
* contain a Serializable Object, and that will be used as the message payload.
* Otherwise, the payload will be a byte array.</li>
* </ul>
* In all cases, the original request headers will be passed in the
* MessageHeaders. Likewise, the following headers will be added:
* <ul>
* <li>{@link HttpHeaders#REQUEST_URL}</li>
* <li>{@link HttpHeaders#REQUEST_METHOD}</li>
* <li>{@link HttpHeaders#USER_PRINCIPAL} (if available)</li>
* </ul>
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @since 1.0.2
*/
public class DefaultInboundRequestMapper implements InboundRequestMapper {
private final Log logger = LogFactory.getLog(getClass());
private volatile MultipartResolver multipartResolver;
private volatile String multipartCharset = null;
private volatile boolean copyUploadedFiles;
/**
* Specify the {@link MultipartResolver} to use when checking requests.
* If no resolver is provided, this mapper will not support multipart
* requests.
*/
public void setMultipartResolver(MultipartResolver multipartResolver) {
this.multipartResolver = multipartResolver;
}
/**
* Specify the charset name to use when converting multipart file content
* into Strings.
*/
public void setMultipartCharset(String multipartCharset) {
this.multipartCharset = multipartCharset;
}
/**
* Specify whether uploaded multipart files should be copied to a temporary
* file on the server. If this is set to 'true', the payload map will
* contain a File instance as the value for each multipart file entry.
* Otherwise the uploaded file's content will be converted to either a
* String or byte array based on the content-type (String for "text/*" and
* byte array otherwise). The default value is false.
*/
public void setCopyUploadedFiles(boolean copyUploadedFiles) {
this.copyUploadedFiles = copyUploadedFiles;
}
public Message<?> toMessage(HttpServletRequest request) throws Exception {
try {
request = this.checkMultipart(request);
Object payload = createPayloadFromRequest(request);
MessageBuilder<?> builder = MessageBuilder.withPayload(payload);
this.populateHeaders(request, builder);
return builder.build();
}
finally {
this.cleanupMultipart(request);
}
}
/**
* Convert the request into a multipart request to make multiparts available.
* If no multipart resolver is set, simply use the existing request.
* @param request current HTTP request
* @return the processed request (multipart wrapper if necessary)
* @see MultipartResolver#resolveMultipart
*/
private HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (request instanceof MultipartHttpServletRequest) {
logger.debug("Request is already a MultipartHttpServletRequest");
}
else {
return this.multipartResolver.resolveMultipart(request);
}
}
return request;
}
/**
* Clean up any resources used by the given multipart request (if any).
* @param request current HTTP request
* @see MultipartResolver#cleanupMultipart
*/
private void cleanupMultipart(HttpServletRequest request) {
if (this.multipartResolver != null && request instanceof MultipartHttpServletRequest) {
this.multipartResolver.cleanupMultipart((MultipartHttpServletRequest) request);
}
}
private Object createPayloadFromRequest(HttpServletRequest request) throws Exception {
Object payload = null;
String contentType = request.getContentType() != null ? request.getContentType() : "";
if (request instanceof MultipartHttpServletRequest) {
payload = this.createPayloadFromMultipartRequest((MultipartHttpServletRequest) request);
}
else if (contentType.startsWith("multipart/form-data")) {
throw new IllegalArgumentException("Content-Type of 'multipart/form-data' requires a MultipartResolver." +
" Try configuring a MultipartResolver within the ApplicationContext.");
}
else if (request.getMethod().equals("GET")) {
if (logger.isDebugEnabled()) {
logger.debug("received GET request, using parameter map as payload");
}
payload = this.createPayloadFromParameterMap(request);
}
else if (contentType.startsWith("application/x-www-form-urlencoded")) {
if (logger.isDebugEnabled()) {
logger.debug("received " + request.getMethod()
+ " request with form data, using parameter map as payload");
}
payload = createPayloadFromParameterMap(request);
}
else if (contentType.startsWith("text")) {
if (logger.isDebugEnabled()) {
logger.debug("received " + request.getMethod()
+ " request, creating payload with text content");
}
payload = createPayloadFromTextContent(request);
}
else if (contentType.startsWith("application/x-java-serialized-object")) {
payload = createPayloadFromSerializedObject(request);
}
else {
payload = createPayloadFromInputStream(request);
}
return payload;
}
@SuppressWarnings("unchecked")
private Object createPayloadFromMultipartRequest(MultipartHttpServletRequest multipartRequest) {
Map<String, Object> payloadMap = new HashMap<String, Object>(multipartRequest.getParameterMap());
Map<String, MultipartFile> fileMap = (Map<String, MultipartFile>) multipartRequest.getFileMap();
for (Map.Entry<String, MultipartFile> entry : fileMap.entrySet()) {
MultipartFile multipartFile = entry.getValue();
if (multipartFile.isEmpty()) {
continue;
}
try {
if (this.copyUploadedFiles) {
File tmpFile = File.createTempFile("si_", null);
multipartFile.transferTo(tmpFile);
payloadMap.put(entry.getKey(), tmpFile);
if (logger.isDebugEnabled()) {
logger.debug("copied uploaded file [" + multipartFile.getOriginalFilename() +
"] to temporary file [" + tmpFile.getAbsolutePath() + "]");
}
}
else if (multipartFile.getContentType() != null && multipartFile.getContentType().startsWith("text")) {
String multipartFileAsString = this.multipartCharset != null ?
new String(multipartFile.getBytes(), this.multipartCharset) :
new String(multipartFile.getBytes());
payloadMap.put(entry.getKey(), multipartFileAsString);
}
else {
payloadMap.put(entry.getKey(), multipartFile.getBytes());
}
}
catch (IOException e) {
throw new IllegalArgumentException("Cannot read contents of multipart file", e);
}
}
return Collections.unmodifiableMap(payloadMap);
}
@SuppressWarnings("unchecked")
private Object createPayloadFromParameterMap(HttpServletRequest request) {
return new UnmodifiableRequestParameterMap(request.getParameterMap());
}
private Object createPayloadFromTextContent(HttpServletRequest request) throws IOException {
String charset = request.getCharacterEncoding() != null ? request.getCharacterEncoding() : "utf-8";
return new String(FileCopyUtils.copyToByteArray(request.getInputStream()), charset);
}
private Object createPayloadFromSerializedObject(HttpServletRequest request) {
try {
return new ObjectInputStream(request.getInputStream()).readObject();
}
catch (Exception e) {
throw new IllegalArgumentException("failed to deserialize Object in request", e);
}
}
private byte[] createPayloadFromInputStream(HttpServletRequest request) throws Exception {
InputStream stream = request.getInputStream();
int length = request.getContentLength();
if (length == -1) {
throw new ResponseStatusCodeException(HttpServletResponse.SC_LENGTH_REQUIRED);
}
if (logger.isDebugEnabled()) {
logger.debug("received " + request.getMethod() + " request, "
+ "creating byte array payload with content lenth: " + length);
}
byte[] bytes = new byte[length];
stream.read(bytes, 0, length);
return bytes;
}
private void populateHeaders(HttpServletRequest request, MessageBuilder<?> builder) {
Enumeration<?> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
Enumeration<?> headerEnum = request.getHeaders(headerName);
if (headerEnum != null) {
List<Object> headers = new ArrayList<Object>();
while (headerEnum.hasMoreElements()) {
headers.add(headerEnum.nextElement());
}
if (headers.size() == 1) {
builder.setHeader(headerName, headers.get(0));
}
else if (headers.size() > 1) {
builder.setHeader(headerName, headers);
}
}
}
}
builder.setHeader(HttpHeaders.REQUEST_URL, request.getRequestURL().toString());
builder.setHeader(HttpHeaders.REQUEST_METHOD, request.getMethod());
builder.setHeader(HttpHeaders.USER_PRINCIPAL, request.getUserPrincipal());
}
/**
* Map class that extends {@link LinkedMultiValueMap} and implements Serializable.
* The contents of the map are unmodifiable, so calling any modification operation
* (e.g. put, add, or remove) will result in an UnsupportedOperationException.
*/
private static class UnmodifiableRequestParameterMap
extends LinkedMultiValueMap<String, String> implements Serializable { // TODO: in 3.0.1 LMVM implements Serializable
UnmodifiableRequestParameterMap(Map<String, String[]> parameters) {
for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
super.put(entry.getKey(), Arrays.asList(entry.getValue()));
}
}
@Override
public void add(String key, String value) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public List<String> put(String key, List<String> value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends String, ? extends List<String>> m) {
throw new UnsupportedOperationException();
}
@Override
public List<String> remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void set(String key, String value) {
throw new UnsupportedOperationException();
}
@Override
public void setAll(Map<String, String> values) {
throw new UnsupportedOperationException();
}
@Override
public Map<String, String> toSingleValueMap() {
return Collections.unmodifiableMap(super.toSingleValueMap());
}
}
}

View File

@@ -0,0 +1,379 @@
/*
* 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.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageDeliveryException;
import org.springframework.util.Assert;
/**
* Default implementation of {@link OutboundRequestMapper}.
*
* @author Mark Fisher
* @since 1.0.2
*/
public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
private volatile URL defaultUrl;
private volatile boolean extractPayload = true;
private volatile String charset = "UTF-8";
/**
* Create a DefaultOutboundRequestMapper with no default URL.
*/
public DefaultOutboundRequestMapper() {
}
/**
* Create a DefaultOutboundRequestMapper with the given default URL.
*/
public DefaultOutboundRequestMapper(URL defaultUrl) {
this.defaultUrl = defaultUrl;
}
/**
* Specify the default URL to use when the outbound message does not
* contain a value for the {@link HttpHeaders#REQUEST_URL} header.
* This default is optional, but if no value is provided, and a Message
* does not contain the header, then a MessageDeliveryException will be
* thrown at runtime.
*/
public void setDefaultUrl(URL defaultUrl) {
this.defaultUrl = defaultUrl;
}
/**
* Specify whether the outbound message's payload should be extracted
* when preparing the request body. Otherwise the Message instance itself
* will be serialized. The default value is <code>true</code>.
*/
public void setExtractPayload(boolean extractPayload) {
this.extractPayload = extractPayload;
}
/**
* Specify the charset name to use for converting String-typed payloads to
* bytes. The default is 'UTF-8'.
*/
public void setCharset(String charset) {
Assert.isTrue(Charset.isSupported(charset), "unsupported charset '" + charset + "'");
this.charset = charset;
}
public HttpRequest fromMessage(Message<?> message) throws Exception {
Assert.notNull(message, "message must not be null");
URL url = this.resolveUrl(message);
if (url == null) {
throw new MessageDeliveryException(message, "failed to determine a target URL for Message");
}
Object requestMethodHeader = message.getHeaders().get(HttpHeaders.REQUEST_METHOD);
String requestMethod = (requestMethodHeader != null) ?
requestMethodHeader.toString().toUpperCase() : "POST";
if (this.extractPayload) {
Object payload = message.getPayload();
Assert.notNull(payload, "payload must not be null");
return this.createRequestFromPayload(payload, url, requestMethod);
}
return this.createRequestFromMessage(message, url, requestMethod);
}
@SuppressWarnings("unchecked")
private HttpRequest createRequestFromPayload(Object payload, URL url, String requestMethod) throws Exception {
ByteArrayOutputStream requestBody = new ByteArrayOutputStream();
String contentType = null;
if ("POST".equals(requestMethod) || "PUT".equals(requestMethod)) {
contentType = this.writeToRequestBody(payload, requestBody);
}
else {
Assert.isTrue(payload instanceof Map,
"Message payload must be a Map for a '" + requestMethod + "' request.");
Map<String, String[]> parameterMap = this.createParameterMap((Map<?,?>) payload);
Assert.notNull(parameterMap, "Payload must be a Map with String typed keys and " +
"String or String array typed values for a '" + requestMethod + "' request.");
url = this.addQueryParametersToUrl(url, parameterMap);
}
return new DefaultHttpRequest(url, requestMethod, requestBody, contentType);
}
private HttpRequest createRequestFromMessage(Message<?> message, URL url, String requestMethod) throws Exception {
Assert.isTrue("POST".equals(requestMethod) || "PUT".equals(requestMethod),
"POST or PUT request method is required when the 'extractPayload' value is false.");
ByteArrayOutputStream requestBody = new ByteArrayOutputStream();
String contentType = this.writeToRequestBody(message, requestBody);
return new DefaultHttpRequest(url, requestMethod, requestBody, contentType);
}
/**
* Creates a parameter map with String keys and String array values from
* the provided map if possible. If the provided map contains any keys that
* are not String typed, or any values that are not String or String array
* typed, then this method will return <code>null</code>.
*/
private Map<String, String[]> createParameterMap(Map<?,?> map) {
Map<String, String[]> parameterMap = new HashMap<String, String[]>();
for (Object key : map.keySet()) {
if (!(key instanceof String)) {
return null;
}
String[] stringArrayValue = null;
Object value = map.get(key);
if (value instanceof String) {
stringArrayValue = new String[] { (String) value };
}
else if (value instanceof String[]) {
stringArrayValue = (String[]) value;
}
else {
return null;
}
parameterMap.put((String) key, stringArrayValue);
}
return parameterMap;
}
@SuppressWarnings("unchecked")
private String writeToRequestBody(Object object, ByteArrayOutputStream byteStream) throws Exception {
String contentType = null;
if (object instanceof byte[]) {
byteStream.write((byte[]) object);
contentType = "application/octet-stream";
}
else if (object instanceof String) {
byteStream.write(((String) object).getBytes(this.charset));
contentType = "text/plain; charset=" + this.charset;
}
else {
if (object instanceof Map && isFormData((Map) object)) {
byte[] data = this.formDataAsBytes((Map) object);
if (data != null) {
byteStream.write(data);
contentType = "application/x-www-form-urlencoded";
}
}
if (contentType == null && object instanceof Serializable) {
byteStream.write(this.serializeObject((Serializable) object));
contentType = "application/x-java-serialized-object";
}
}
if (contentType == null) {
throw new IllegalArgumentException("payload must be a byte array, " +
"String, Map, or Serializable object for a 'POST' or 'PUT' request");
}
return contentType;
}
/**
* If all keys are Strings, we'll consider the Map to be form data.
*/
@SuppressWarnings("unchecked")
private boolean isFormData(Map map) {
for (Object key : map.keySet()) {
if (!(key instanceof String)) {
return false;
}
}
return true;
}
@SuppressWarnings("unchecked")
private byte[] formDataAsBytes(Map form) throws UnsupportedEncodingException {
StringBuilder builder = new StringBuilder();
Iterator<?> nameIterator = form.keySet().iterator();
while (nameIterator.hasNext()) {
Object next = nameIterator.next();
Assert.isTrue(next instanceof String, "Form map keys must be Strings.");
String name = (String) next;
Object value = form.get(name);
if (value == null) {
builder.append(URLEncoder.encode(name, this.charset));
}
else {
List<String> values = null;
if (value instanceof String) {
values = Collections.singletonList((String) value);
}
else if (value instanceof String[]) {
values = Arrays.asList((String[]) value);
}
else {
if (!(value instanceof Iterable)) {
return null;
}
Iterator iterator = ((Iterable) value).iterator();
values = new ArrayList<String>();
while (iterator.hasNext()) {
Object nextValue = iterator.next();
if (!(nextValue instanceof String)) {
return null;
}
values.add((String) nextValue);
}
}
Iterator<String> valueIterator = values.iterator();
builder.append(URLEncoder.encode(name, this.charset));
while (valueIterator.hasNext()) {
builder.append('=' + URLEncoder.encode(valueIterator.next(), this.charset));
if (valueIterator.hasNext()) {
builder.append('&' + URLEncoder.encode(name, this.charset));
}
}
}
if (nameIterator.hasNext()) {
builder.append('&');
}
}
return builder.toString().getBytes(this.charset);
}
private byte[] serializeObject(Serializable object) throws IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
objectStream.writeObject(object);
objectStream.flush();
objectStream.close();
return byteStream.toByteArray();
}
/**
* Resolve the request URL for the given Message. This implementation
* returns the value associated with the {@link HttpHeaders#REQUEST_URL}
* key if available in the Message's headers. Otherwise, it falls back to
* the default URL as provided to the constructor of this mapper instance.
* @throws MalformedURLException if an error occurs while constructing the URL
*/
private URL resolveUrl(Message<?> message) throws MalformedURLException {
Object urlHeader = message.getHeaders().get(HttpHeaders.REQUEST_URL);
if (urlHeader == null) {
return this.defaultUrl;
}
if (urlHeader instanceof URL) {
return (URL) urlHeader;
}
if (urlHeader instanceof URI) {
return ((URI) urlHeader).toURL();
}
if (urlHeader instanceof String) {
return new URL((String) urlHeader);
}
throw new IllegalArgumentException("Target URL in Message header must be a URL, URI, or String.");
}
/**
* Constructs a query string by appending the parameter map values to the URL.
* @throws Exception if an error occurs encoding or constructing the URL
*/
private URL addQueryParametersToUrl(URL url, Map<String, String[]> parameterMap) throws Exception {
if (parameterMap == null || parameterMap.size() == 0) {
return url;
}
String urlString = url.toExternalForm();
String fragment = "";
int fragmentStartIndex = urlString.indexOf('#');
if (fragmentStartIndex != -1) {
fragment = urlString.substring(fragmentStartIndex);
urlString = urlString.substring(0, fragmentStartIndex);
}
StringBuilder sb = new StringBuilder(urlString);
if (urlString.indexOf('?') == -1) {
sb.append('?');
}
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String[] values = entry.getValue();
for (String value : values) {
char lastChar = sb.charAt(sb.length() -1);
if (lastChar != '?' && lastChar != '&') {
sb.append('&');
}
sb.append(URLEncoder.encode(entry.getKey(), this.charset) + "=");
sb.append(URLEncoder.encode(value, this.charset));
}
}
sb.append(fragment);
return new URL(sb.toString());
}
/**
* Default implementation of {@link HttpRequest}.
*/
class DefaultHttpRequest implements HttpRequest {
private final URL targetUrl;
private final String requestMethod;
private final String contentType;
private volatile ByteArrayOutputStream requestBody;
DefaultHttpRequest(
URL targetUrl, String requestMethod, ByteArrayOutputStream requestBody, String contentType)
throws IOException {
Assert.notNull(targetUrl, "target url must not be null");
this.targetUrl = targetUrl;
this.requestMethod = (requestMethod != null) ? requestMethod : "POST";
this.requestBody = requestBody;
this.contentType = contentType;
}
public URL getTargetUrl() {
return this.targetUrl;
}
public String getRequestMethod() {
return this.requestMethod;
}
public String getContentType() {
return this.contentType;
}
public Integer getContentLength() {
return (this.requestBody != null) ? this.requestBody.size() : null;
}
public ByteArrayOutputStream getBody() {
return this.requestBody;
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 org.springframework.integration.core.MessageHeaders;
/**
* @author Mark Fisher
* @since 1.0.2
*/
public abstract class HttpHeaders {
private static final String PREFIX = MessageHeaders.PREFIX + "http_";
public static final String REQUEST_URL = PREFIX + "requestUrl";
public static final String REQUEST_METHOD = PREFIX + "requestMethod";
public static final String USER_PRINCIPAL = PREFIX + "userPrincipal";
}

View File

@@ -0,0 +1,301 @@
/*
* 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 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.
* <p/>
* 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}.
* <p/>
* 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.
* <p/>
* The value for {@link #expectReply} is <code>false</code> 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 <code>false</code> then a simple OK status response will be issued.
* <p/>
* To handle request-reply scenarios, set the 'expectReply' flag to
* <code>true</code>. 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 <code>false</code>.
* <p/>
* 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<String> 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 <code>null</code>, the String or byte array
* will be written directly to the HTTP response.
* <p>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 = (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<String, Object> model = new HashMap<String, Object>();
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");
}
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.net.URL;
import org.springframework.integration.core.Message;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
/**
* An outbound endpoint that maps a request Message to an {@link HttpRequest},
* executes that request, and then maps the response to a reply Message.
*
* @author Mark Fisher
* @since 1.0.2
*/
public class HttpOutboundEndpoint extends AbstractReplyProducingMessageHandler {
private volatile OutboundRequestMapper requestMapper;
private volatile HttpRequestExecutor requestExecutor = new SimpleHttpRequestExecutor();
/**
* Create an HttpOutboundEndpoint with no default URL.
*/
public HttpOutboundEndpoint() {
this.requestMapper = new DefaultOutboundRequestMapper();
}
/**
* Create an HttpOutboundEndpoint that will send requests to the provided
* URL by default. If a Message contains a valid value for the
* {@link HttpHeaders#REQUEST_URL} header, that will take precedence.
* If a custom {@link OutboundRequestMapper} instance is registered
* through the {@link #setRequestMapper(OutboundRequestMapper)} method,
* this default URL will not be used.
*/
public HttpOutboundEndpoint(URL defaultUrl) {
this.requestMapper = new DefaultOutboundRequestMapper(defaultUrl);
}
/**
* Specify an {@link OutboundRequestMapper} implementation to map from
* Messages to outbound {@link HttpRequest} objects. The default
* implementation is {@link DefaultOutboundRequestMapper}.
*/
public void setRequestMapper(OutboundRequestMapper requestMapper) {
Assert.notNull(requestMapper, "requestMapper must not be null");
this.requestMapper = requestMapper;
}
/**
* Specify the {@link HttpRequestExecutor} to use for executing the
* {@link HttpRequest} instances at runtime. The default implementation
* is {@link SimpleHttpRequestExecutor}.
*/
public void setRequestExecutor(HttpRequestExecutor requestExecutor) {
Assert.notNull(requestExecutor, "requestExecutor must not be null");
this.requestExecutor = requestExecutor;
}
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
try {
HttpRequest request = this.requestMapper.fromMessage(requestMessage);
HttpResponse response = this.requestExecutor.executeRequest(request);
Object reply = this.createReplyFromResponse(response);
return reply;
}
catch (Exception e) {
throw new MessageHandlingException(requestMessage, "failed to execute HTTP request", e);
}
}
private Object createReplyFromResponse(HttpResponse response) throws Exception {
InputStream responseBody = response.getBody();
Assert.notNull(responseBody, "received null response body");
String contentType = response.getFirstHeader("Content-Type");
if (contentType != null && contentType.startsWith("application/x-java-serialized-object")) {
// may be either a payload or a serialized Message instance
return this.deserializePayload(responseBody);
}
ByteArrayOutputStream responseByteStream = new ByteArrayOutputStream();
FileCopyUtils.copy(responseBody, responseByteStream);
if (contentType != null && contentType.startsWith("text")) {
String charsetName = this.getCharsetName(response);
if (charsetName == null) {
charsetName = "ISO-8859-1";
}
return responseByteStream.toString(charsetName);
}
return responseByteStream.toByteArray();
}
private String getCharsetName(HttpResponse httpResponse) {
String contentType = httpResponse.getFirstHeader("Content-Type");
if (contentType != null) {
int beginIndex = contentType.indexOf("charset=");
if (beginIndex != -1) {
return contentType.substring(beginIndex + "charset=".length()).trim();
}
}
return null;
}
private Object deserializePayload(InputStream responseBody) throws IOException, ClassNotFoundException {
ObjectInputStream objectStream = null;
try {
objectStream = new ObjectInputStream(responseBody);
return objectStream.readObject();
}
catch (ObjectStreamException e) {
throw new IllegalArgumentException("failed to deserialize response", e);
}
finally {
try {
objectStream.close();
}
catch (Exception e) {
// ignore
}
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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 java.io.ByteArrayOutputStream;
import java.net.URL;
/**
* Representation of an HTTP request to be executed by an implementation of
* the {@link HttpRequestExecutor} strategy.
*
* @author Mark Fisher
* @since 1.0.2
*/
public interface HttpRequest {
/**
* Return the target URL for this request.
*/
URL getTargetUrl();
/**
* Return the request method ("GET", "POST", etc).
*/
String getRequestMethod();
/**
* Return the content type for requests.
*/
String getContentType();
/**
* Return the content length if known, else <code>null</code>.
*/
Integer getContentLength();
/**
* Return the request body as a {@link ByteArrayOutputStream},
* or <code>null</code> if this request has no body content.
*/
ByteArrayOutputStream getBody();
}

View File

@@ -0,0 +1,30 @@
/*
* 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;
/**
* Strategy for executing an http request response exchange with a remote server.
*
* @author Iwein Fuld
* @author Mark Fisher
* @since 1.0.2
*/
public interface HttpRequestExecutor {
HttpResponse executeRequest(HttpRequest request) throws Exception;
}

View File

@@ -0,0 +1,53 @@
/*
* 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 java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* Representation of an HTTP response as returned by an implementation of
* the {@link HttpRequestExecutor} strategy.
*
* @author Mark Fisher
* @since 1.0.2
*/
public interface HttpResponse {
/**
* Return all response headers as a map. There may be multiple values per
* key in the map. Hence, the value type is a List of Strings.
*/
Map<String, List<String>> getHeaders();
/**
* Return all header values for a given key, or null if it has no values.
*/
List<String> getHeaders(String key);
/**
* Return the first header value for a given key, or null if it has no values.
*/
String getFirstHeader(String key);
/**
* Return the body of the response as an InputStream.
*/
InputStream getBody();
}

View File

@@ -0,0 +1,32 @@
/*
* 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 javax.servlet.http.HttpServletRequest;
import org.springframework.integration.message.InboundMessageMapper;
/**
* Strategy interface for mapping from an inbound {@link HttpServletRequest}
* to a Message.
*
* @author Mark Fisher
* @since 1.0.2
*/
public interface InboundRequestMapper extends InboundMessageMapper<HttpServletRequest> {
}

View File

@@ -0,0 +1,29 @@
/*
* 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 org.springframework.integration.message.OutboundMessageMapper;
/**
* Strategy for mapping to an {@link HttpRequest} from a message.
*
* @author Mark Fisher
* @since 1.0.2
*/
public interface OutboundRequestMapper extends OutboundMessageMapper<HttpRequest> {
}

View File

@@ -0,0 +1,41 @@
/*
* 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;
/**
* Exception that provides a response status code. This can be used by
* {@link InboundRequestMapper} implementations to indicate an error.
*
* @author Mark Fisher
* @since 1.0.2
*/
@SuppressWarnings("serial")
public class ResponseStatusCodeException extends Exception {
private final int statusCode;
public ResponseStatusCodeException(int statusCode) {
this.statusCode = statusCode;
}
public int getStatusCode() {
return this.statusCode;
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
/**
* Implementation of {@link HttpRequestExecutor} that uses {@link HttpURLConnection}
* directly. This version has limited functionality but no additional dependencies.
* For more features, see {@link CommonsHttpRequestExecutor}.
*
* @author Juergen Hoeller
* @author Iwein Fuld
* @author Mark Fisher
* @since 1.0.2
*/
public class SimpleHttpRequestExecutor extends AbstractHttpRequestExecutor {
@Override
protected HttpResponse doExecuteRequest(HttpRequest request) throws Exception {
HttpURLConnection connection = this.openConnection(request.getTargetUrl());
this.prepareConnection(connection, request);
this.writeRequestBody(connection, request.getBody());
this.validateResponse(connection);
InputStream responseBody = this.readResponseBody(connection);
return new DefaultHttpResponse(responseBody, this.getResponseHeaders(connection));
}
/**
* Open an HttpURLConnection for the given request URL.
* @return the HttpURLConnection for the given request
* @throws IOException if thrown by I/O methods
* @see java.net.URL#openConnection()
*/
private HttpURLConnection openConnection(URL url) throws IOException {
URLConnection connection = url.openConnection();
if (!(connection instanceof HttpURLConnection)) {
throw new IOException("target URL [" + url + "] is not an HTTP URL");
}
return (HttpURLConnection) connection;
}
/**
* Prepare the given HTTP connection.
* <p>
* The request method (e.g. "POST), "Content-Type" header, and content
* length will be determined from the provided {@link HttpRequest}.
* @param connection HttpURLConnection the connection to prepare
* @param request HttpRequest for which the connection should be prepared
* @throws IOException if thrown by HttpURLConnection methods
* @see java.net.HttpURLConnection#setRequestMethod
* @see java.net.HttpURLConnection#setRequestProperty
*/
private void prepareConnection(HttpURLConnection connection, HttpRequest request) throws IOException {
connection.setDoInput(true);
String requestMethod = request.getRequestMethod();
if ("PUT".equals(requestMethod) || "POST".equals(requestMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
connection.setRequestMethod(request.getRequestMethod());
String contentType = request.getContentType();
if (contentType != null) {
connection.setRequestProperty(HTTP_HEADER_CONTENT_TYPE, contentType);
}
Integer contentLength = request.getContentLength();
if (contentLength != null) {
connection.setRequestProperty(HTTP_HEADER_CONTENT_LENGTH, contentLength.toString());
}
LocaleContext locale = LocaleContextHolder.getLocaleContext();
if (locale != null) {
connection.setRequestProperty(HTTP_HEADER_ACCEPT_LANGUAGE,
StringUtils.toLanguageTag(locale.getLocale()));
}
if (isAcceptGzipEncoding()) {
connection.setRequestProperty(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
}
}
private void writeRequestBody(HttpURLConnection connection, ByteArrayOutputStream body) throws IOException {
if (body != null) {
byte[] bytes = body.toByteArray();
if (bytes.length > 0) {
FileCopyUtils.copy(bytes, connection.getOutputStream());
}
}
}
private void validateResponse(HttpURLConnection connection) throws IOException {
if (connection.getResponseCode() >= 300) {
throw new IOException(
"Did not receive successful HTTP response from [" + connection.getURL() +
"]: status code = " + connection.getResponseCode() +
", status message = [" + connection.getResponseMessage() + "]");
}
}
/**
* Extract the response body from the connection after the request has
* been successfully executed.
* <p>
* This implementation simply reads the HttpURLConnection's InputStream.
* If the response is recognized as GZIP response, the InputStream will be
* wrapped in a GZIPInputStream.
* @param connection the HttpURLConnection to read the response body from
* @return an InputStream for the response body
* @throws IOException if thrown by I/O methods
* @see #isGzipResponse
* @see java.util.zip.GZIPInputStream
* @see java.net.HttpURLConnection#getInputStream()
*/
private InputStream readResponseBody(HttpURLConnection connection) throws IOException {
if (isGzipResponse(connection)) {
// GZIP response found - need to unzip.
return new GZIPInputStream(connection.getInputStream());
}
else {
// Plain response found.
return connection.getInputStream();
}
}
private Map<String, List<String>> getResponseHeaders(HttpURLConnection connection) {
return connection.getHeaderFields();
}
/**
* Determine whether the given response is a GZIP response.
* <p>
* This implementation checks whether the HTTP "Content-Encoding" header
* contains "gzip" (in any casing).
* @param connection the HttpURLConnection to check
*/
private boolean isGzipResponse(HttpURLConnection connection) {
String encodingHeader = connection.getHeaderField(HTTP_HEADER_CONTENT_ENCODING);
return (encodingHeader != null
&& encodingHeader.toLowerCase().indexOf(ENCODING_GZIP) != -1);
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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.config;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for the 'inbound-channel-adapter' and 'inbound-gateway' elements
* of the 'http' namespace. The constructor's boolean value specifies whether
* a reply is to be expected. This value should be 'false' for the
* 'inbound-channel-adapter' and 'true' for the 'inbound-gateway'.
*
* @author Mark Fisher
*/
public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParser {
private final boolean expectReply;
public HttpInboundEndpointParser(boolean expectReply) {
this.expectReply = expectReply;
}
@Override
protected String getBeanClassName(Element element) {
return "org.springframework.integration.http.HttpInboundEndpoint";
}
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
id = element.getAttribute("name");
}
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error("The 'id' or 'name' is required.", element);
}
return id;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String inputChannelAttributeName = this.getInputChannelAttributeName();
String inputChannelRef = element.getAttribute(inputChannelAttributeName);
if (!StringUtils.hasText(inputChannelRef)) {
parserContext.getReaderContext().error(
"a '" + inputChannelAttributeName + "' reference is required", element);
}
builder.addPropertyReference("requestChannel", inputChannelRef);
builder.addPropertyValue("expectReply", this.expectReply);
if (this.expectReply) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-reply-payload");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-key");
}
else {
IntegrationNamespaceUtils.setValueIfAttributeDefined(
builder, element, "send-timeout", "requestTimeout");
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "supported-methods");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "view");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-key");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-mapper");
}
private String getInputChannelAttributeName() {
return this.expectReply ? "request-channel" : "channel";
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.config;
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
/**
* Namespace handler for Spring Integration's <em>http</em> namespace.
*
* @author Mark Fisher
* @since 1.0.2
*/
public class HttpNamespaceHandler extends AbstractIntegrationNamespaceHandler {
public void init() {
this.registerBeanDefinitionParser("inbound-channel-adapter", new HttpInboundEndpointParser(false));
this.registerBeanDefinitionParser("inbound-gateway", new HttpInboundEndpointParser(true));
this.registerBeanDefinitionParser("outbound-gateway", new HttpOutboundGatewayParser());
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
/**
* Parser for the 'outbound-gateway' element of the http namespace.
*
* @author Mark Fisher
*/
public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
private static final String PACKAGE_PATH = "org.springframework.integration.http";
@Override
protected String getInputChannelAttributeName() {
return "request-channel";
}
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
String defaultUrl = element.getAttribute("default-url");
String charset = element.getAttribute("charset");
String extractPayload = element.getAttribute("extract-request-payload");
String requestMapperRef = element.getAttribute("request-mapper");
if (StringUtils.hasText(requestMapperRef)) {
if (StringUtils.hasText(defaultUrl)) {
this.requestMapperConflictError("default-url", parserContext, element);
return null;
}
else if (StringUtils.hasText(charset)) {
this.requestMapperConflictError("charset", parserContext, element);
return null;
}
else if (StringUtils.hasText(extractPayload)) {
this.requestMapperConflictError("extract-request-payload", parserContext, element);
return null;
}
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
PACKAGE_PATH + ".HttpOutboundEndpoint");
if (!StringUtils.hasText(requestMapperRef)) {
BeanDefinitionBuilder mapperBuilder = BeanDefinitionBuilder.genericBeanDefinition(
PACKAGE_PATH + ".DefaultOutboundRequestMapper");
if (StringUtils.hasText(defaultUrl)) {
mapperBuilder.addConstructorArgValue(defaultUrl);
}
if (StringUtils.hasText(charset)) {
mapperBuilder.addPropertyValue("charset", charset);
}
if (StringUtils.hasText(extractPayload)) {
mapperBuilder.addPropertyValue("extractPayload", extractPayload);
}
requestMapperRef = BeanDefinitionReaderUtils.registerWithGeneratedName(
mapperBuilder.getBeanDefinition(), parserContext.getRegistry());
}
builder.addPropertyReference("requestMapper", requestMapperRef);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout", "sendTimeout");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-executor");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
return builder;
}
private void requestMapperConflictError(String nameForGateway, ParserContext parserContext, Element element) {
parserContext.getReaderContext().error("The '" + nameForGateway + "' and 'request-mapper' are mutually exclusive. " +
"When providing an OutboundRequestMapper, set any corresponding property on the mapper directly.", element);
}
}

View File

@@ -0,0 +1 @@
http\://www.springframework.org/schema/integration/http=org.springframework.integration.http.config.HttpNamespaceHandler

View File

@@ -0,0 +1,3 @@
http\://www.springframework.org/schema/integration/http/spring-integration-http-1.0.xsd=org/springframework/integration/http/config/spring-integration-http-1.0.xsd
http\://www.springframework.org/schema/integration/http/spring-integration-http-2.0.xsd=org/springframework/integration/http/config/spring-integration-http-2.0.xsd
http\://www.springframework.org/schema/integration/http/spring-integration-http.xsd=org/springframework/integration/http/config/spring-integration-http-2.0.xsd

View File

@@ -0,0 +1,4 @@
# Tooling related information for the integration http namespace
http\://www.springframework.org/schema/integration/http@name=integration http Namespace
http\://www.springframework.org/schema/integration/http@prefix=int-http
http\://www.springframework.org/schema/integration/http@icon=org/springframework/integration/http/config/spring-integration-http.gif

View File

@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/integration/http"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for Spring Integration's HTTP adapters.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="inbound-channel-adapter">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines an inbound HTTP-based Channel Adapter.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:ID"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="send-timeout" type="xsd:string"/>
<xsd:attribute name="supported-methods" type="xsd:string"/>
<xsd:attribute name="view" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.web.servlet.View"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-mapper" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.http.InboundRequestMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-key" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="inbound-gateway">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines an inbound HTTP-based Messaging Gateway.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="gatewayType">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="extract-reply-payload" type="xsd:string" default="true"/>
<xsd:attribute name="supported-methods" type="xsd:string"/>
<xsd:attribute name="view" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.web.servlet.View"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-mapper" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.http.InboundRequestMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-key" type="xsd:string"/>
<xsd:attribute name="reply-key" type="xsd:string"/>
<xsd:attribute name="reply-timeout" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-gateway">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines an outbound HTTP-based Messaging Gateway.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="gatewayType">
<xsd:attribute name="default-url" type="xsd:string"/>
<xsd:attribute name="extract-request-payload" type="xsd:string"/>
<xsd:attribute name="charset" type="xsd:string"/>
<xsd:attribute name="request-mapper" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.http.OutboundRequestMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-executor" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.http.HttpRequestExecutor"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies the order for invocation when this gateway is connected as a
subscriber to a SubscribableChannel.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="gatewayType">
<xsd:annotation>
<xsd:documentation>
Defines common configuration for gateway adapters.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:ID"/>
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-timeout" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/integration/http"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for Spring Integration's HTTP adapters.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="inbound-channel-adapter">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines an inbound HTTP-based Channel Adapter.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:ID"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="send-timeout" type="xsd:string"/>
<xsd:attribute name="supported-methods" type="xsd:string"/>
<xsd:attribute name="view" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.web.servlet.View"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-mapper" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.http.InboundRequestMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-key" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="inbound-gateway">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines an inbound HTTP-based Messaging Gateway.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="gatewayType">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="extract-reply-payload" type="xsd:string" default="true"/>
<xsd:attribute name="supported-methods" type="xsd:string"/>
<xsd:attribute name="view" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.web.servlet.View"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-mapper" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.http.InboundRequestMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-key" type="xsd:string"/>
<xsd:attribute name="reply-key" type="xsd:string"/>
<xsd:attribute name="reply-timeout" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-gateway">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines an outbound HTTP-based Messaging Gateway.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="gatewayType">
<xsd:attribute name="default-url" type="xsd:string"/>
<xsd:attribute name="extract-request-payload" type="xsd:string"/>
<xsd:attribute name="charset" type="xsd:string"/>
<xsd:attribute name="request-mapper" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.http.OutboundRequestMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-executor" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.http.HttpRequestExecutor"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies the order for invocation when this gateway is connected as a
subscriber to a SubscribableChannel.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="gatewayType">
<xsd:annotation>
<xsd:documentation>
Defines common configuration for gateway adapters.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:ID"/>
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-timeout" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,127 @@
/*
* 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.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.integration.core.Message;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;
/**
* @author Iwein Fuld
* @author Mark Fisher
*/
@SuppressWarnings("unchecked")
public class DefaultInboundRequestMapperTests {
private static final String SIMPLE_STRING = "just ascii";
private static final String COMPLEX_STRING = "A\u00ea\u00f1\u00fcC";
private DefaultInboundRequestMapper mapper = new DefaultInboundRequestMapper();
@Test
public void simpleUtf8TextMapping() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
request.setCharacterEncoding("utf-8");
byte[] bytes = SIMPLE_STRING.getBytes("utf-8");
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
assertThat(message.getPayload(), is(SIMPLE_STRING));
}
@Test
public void complexUtf8TextMapping() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
// don't forget to specify the character encoding on the request or you
// will end up with unpredictable results!
request.setCharacterEncoding("utf-8");
byte[] bytes = COMPLEX_STRING.getBytes("utf-8");
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
assertThat(message.getPayload(), is(COMPLEX_STRING));
}
@Test
public void newlineTest() throws Exception {
String content = "foo\nbar\n";
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
byte[] bytes = content.getBytes();
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
assertThat(message.getPayload(), is(content));
}
@Test
public void emptyStringTest() throws Exception {
String content = "";
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
byte[] bytes = content.getBytes();
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
assertThat(message.getPayload(), is(content));
}
@Test
public void multipartUpload() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<String, MultipartFile>();
MultipartFile file = new StubMultipartFile("file", "testFile.txt", "foo");
files.add("file", file);
Map<String, String[]> params = new HashMap<String, String[]>();
MultipartHttpServletRequest multipartRequest = new DefaultMultipartHttpServletRequest(request, files, params);
mapper.setCopyUploadedFiles(true);
Message<?> result = mapper.toMessage(multipartRequest);
File tmpFile = (File) ((Map) result.getPayload()).get("file");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileCopyUtils.copy(new FileInputStream(tmpFile), baos);
assertThat(baos.toString(), is("foo"));
tmpFile.deleteOnExit();
}
@Test
public void testProcessMessageWithDollar() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
request.setCharacterEncoding("utf-8");
byte[] bytes = SIMPLE_STRING.getBytes("utf-8");
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("headers['$http_requestUrl']");
assertEquals(message.getHeaders().get(HttpHeaders.REQUEST_URL), processor.processMessage(message));
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageBuilder;
/**
* @author Mark Fisher
*/
public class DefaultOutboundRequestMapperTests {
@Test
public void simpleStringValueFormData() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map<String, String> form = new LinkedHashMap<String, String>();
form.put("a", "1");
form.put("b", "2");
form.put("c", "3");
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a=1&b=2&c=3", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
@SuppressWarnings("unchecked")
public void stringArrayValueFormData() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map form = new LinkedHashMap();
form.put("a", new String[] { "1", "2", "3" });
form.put("b", "4");
form.put("c", new String[] { "5" });
form.put("d", "6");
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a=1&a=2&a=3&b=4&c=5&d=6", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
@SuppressWarnings("unchecked")
public void stringListValueFormData() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map form = new LinkedHashMap();
List<String> listA = new ArrayList<String>();
listA.add("1");
listA.add("2");
form.put("a", listA);
form.put("b", Collections.EMPTY_LIST);
form.put("c", Collections.singletonList("3"));
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a=1&a=2&b&c=3", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
@SuppressWarnings("unchecked")
public void nameOnlyWithNullValues() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map form = new LinkedHashMap();
form.put("a", null);
form.put("b", "foo");
form.put("c", null);
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a&b=foo&c", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
public void encodedFormData() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map<String, String> form = new LinkedHashMap<String, String>();
form.put("a", "1 + 2 + 3");
form.put("b", "4+5");
form.put("c", "97%");
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a=1+%2B+2+%2B+3&b=4%2B5&c=97%25", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
@SuppressWarnings("unchecked")
public void nonFormDataInMap() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map<String, TestBean> form = new LinkedHashMap<String, TestBean>();
form.put("A", new TestBean());
form.put("B", new TestBean());
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
byte[] body = request.getBody().toByteArray();
ByteArrayInputStream byteStream = new ByteArrayInputStream(body);
Object result = new ObjectInputStream(byteStream).readObject();
assertEquals(LinkedHashMap.class, result.getClass());
Map<String, TestBean> resultMap = (Map<String, TestBean>) result;
assertEquals(2, resultMap.size());
assertEquals(TestBean.class, resultMap.get("A").getClass());
assertEquals(TestBean.class, resultMap.get("B").getClass());
}
@SuppressWarnings("serial")
private static class TestBean implements Serializable {
}
}

View File

@@ -0,0 +1,406 @@
/*
* 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<String, String> sourceParams = addAnyParametersToRequest();
expect(requestChannel.send(isA(Message.class))).andAnswer(
new IAnswer<Boolean>() {
@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<String>) 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<Boolean>() {
@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<Boolean>() {
@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<Boolean>() {
@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<Boolean>() {
@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<String, String> sourceParams = addAnyParametersToRequest();
request.setParameters(sourceParams);
expect(requestChannel.send(isA(Message.class))).andAnswer(
new IAnswer<Boolean>() {
@SuppressWarnings("unchecked")
public Boolean answer() throws Throwable {
MultiValueMap<String, String> payloadMap = (MultiValueMap<String, String>)
((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<Boolean>() {
@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<Boolean>() {
@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<Object>() {
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<String, String> addAnyParametersToRequest() {
final Map<String, String> sourceParams = new HashMap<String, String>();
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);
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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 java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.web.multipart.MultipartFile;
/**
* @author Mark Fisher
*/
public class StubMultipartFile implements MultipartFile {
private final String parameterName;
private final String filename;
private final byte[] bytes;
private final String text;
public StubMultipartFile(String parameterName, String filename, String text) {
this.parameterName = parameterName;
this.filename = filename;
if (text != null) {
this.bytes = text.getBytes();
}
else {
this.bytes = null;
}
this.text = text;
}
public StubMultipartFile(String parameterName, String filename, byte[] bytes) {
this.parameterName = parameterName;
this.filename = filename;
this.bytes = bytes;
this.text = null;
}
public byte[] getBytes() throws IOException {
return this.bytes;
}
public String getContentType() {
return (this.text != null) ? "text" : null;
}
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.bytes);
}
public String getName() {
return this.parameterName;
}
public String getOriginalFilename() {
return this.filename;
}
public long getSize() {
return this.bytes.length;
}
public boolean isEmpty() {
return this.bytes == null || this.bytes.length == 0;
}
public void transferTo(File dest) throws IOException, IllegalStateException {
FileOutputStream fos = new FileOutputStream(dest);
fos.write(this.bytes);
fos.close();
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<http:outbound-gateway request-channel="testChannel" auto-startup="false"/>
</beans>

View File

@@ -0,0 +1,72 @@
/*
* 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.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
* @since 1.0.3
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DefaultConfigurationTests {
@Autowired
private ApplicationContext context;
@Test
public void verifyErrorChannel() {
Object errorChannel = context.getBean("errorChannel");
assertNotNull(errorChannel);
assertEquals(PublishSubscribeChannel.class, errorChannel.getClass());
}
@Test
public void verifyNullChannel() {
Object nullChannel = context.getBean("nullChannel");
assertNotNull(nullChannel);
assertEquals(NullChannel.class, nullChannel.getClass());
}
@Test
public void verifyTaskScheduler() {
Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME);
assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass());
Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler");
assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass());
Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel");
assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel);
}
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<si:channel id="requests">
<si:queue capacity="1"/>
</si:channel>
<inbound-channel-adapter id="defaultAdapter" channel="requests"/>
<inbound-channel-adapter id="postOnlyAdapter" channel="requests" supported-methods="POST"/>
<inbound-channel-adapter id="putOrDeleteAdapter" channel="requests" supported-methods="PUT, delete"/>
</beans:beans>

View File

@@ -0,0 +1,151 @@
/*
* 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.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.channel.PollableChannel;
import org.springframework.integration.core.Message;
import org.springframework.integration.http.HttpInboundEndpoint;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.MultiValueMap;
/**
* @author Mark Fisher
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class HttpInboundChannelAdapterParserTests {
@Autowired @Qualifier("requests")
private PollableChannel requests;
@Autowired @Qualifier("defaultAdapter")
private HttpInboundEndpoint defaultAdapter;
@Autowired @Qualifier("postOnlyAdapter")
private HttpInboundEndpoint postOnlyAdapter;
@Autowired @Qualifier("putOrDeleteAdapter")
private HttpInboundEndpoint putOrDeleteAdapter;
@Test
@SuppressWarnings("unchecked")
public void getRequestOk() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("GET");
request.setParameter("foo", "bar");
MockHttpServletResponse response = new MockHttpServletResponse();
defaultAdapter.handleRequest(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
Message<?> message = requests.receive(0);
assertNotNull(message);
Object payload = message.getPayload();
assertTrue(payload instanceof MultiValueMap);
MultiValueMap<String, String> map = (MultiValueMap<String, String>) payload;
assertEquals(1, map.size());
assertEquals("foo", map.keySet().iterator().next());
assertEquals(1, map.get("foo").size());
assertEquals("bar", map.getFirst("foo"));
}
@Test
public void getRequestNotAllowed() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("GET");
request.setParameter("foo", "bar");
MockHttpServletResponse response = new MockHttpServletResponse();
postOnlyAdapter.handleRequest(request, response);
assertEquals(HttpServletResponse.SC_METHOD_NOT_ALLOWED, response.getStatus());
Message<?> message = requests.receive(0);
assertNull(message);
}
@Test
public void postRequestWithTextContentOk() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("POST");
request.setContent("test".getBytes());
request.setContentType("text/plain");
MockHttpServletResponse response = new MockHttpServletResponse();
postOnlyAdapter.handleRequest(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
Message<?> message = requests.receive(0);
assertNotNull(message);
assertEquals("test", message.getPayload());
}
@Test
public void postRequestWithSerializedObjectContentOk() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("POST");
Object obj = new TestObject("testObject");
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
new ObjectOutputStream(byteStream).writeObject(obj);
request.setContent(byteStream.toByteArray());
request.setContentType("application/x-java-serialized-object");
MockHttpServletResponse response = new MockHttpServletResponse();
postOnlyAdapter.handleRequest(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
Message<?> message = requests.receive(0);
assertNotNull(message);
assertTrue(message.getPayload() instanceof TestObject);
assertEquals("testObject", ((TestObject) message.getPayload()).text);
}
@Test
@SuppressWarnings("unchecked")
public void putOrDeleteMethodsSupported() throws Exception {
DirectFieldAccessor accessor = new DirectFieldAccessor(putOrDeleteAdapter);
List<String> supportedMethods = (List<String>) accessor.getPropertyValue("supportedMethods");
assertEquals(2, supportedMethods.size());
assertTrue(supportedMethods.contains("PUT"));
assertTrue(supportedMethods.contains("DELETE"));
}
@SuppressWarnings("serial")
private static class TestObject implements Serializable {
String text;
TestObject(String text) {
this.text = text;
}
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<si:publish-subscribe-channel id="requests"/>
<si:bridge output-channel="responses" input-channel="requests"/>
<si:channel id="responses"/>
<inbound-gateway id="inboundGateway" request-channel="requests"
reply-channel="responses" />
</beans:beans>

View File

@@ -0,0 +1,76 @@
/*
* 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.config;
import static org.hamcrest.CoreMatchers.any;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.channel.SubscribableChannel;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.http.HttpInboundEndpoint;
import static org.springframework.integration.test.util.TestUtils.getPropertyValue;
import static org.springframework.integration.test.util.TestUtils.handlerExpecting;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.servlet.http.HttpServletResponse;
/**
* @author Mark Fisher
* @author Iwein Fuld
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class HttpInboundGatewayParserTests {
@Autowired
private HttpInboundEndpoint gateway;
@Qualifier("responses")
@Autowired
MessageChannel responses;
@Qualifier("requests")
@Autowired
SubscribableChannel requests;
@Test
public void checkConfig() {
assertNotNull(gateway);
assertThat((Boolean) getPropertyValue(gateway, "expectReply"), is(true));
}
@Test(timeout=1000)
public void checkFlow() throws Exception {
requests.subscribe(handlerExpecting(any(Message.class)));
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("GET");
request.setParameter("foo", "bar");
MockHttpServletResponse response = new MockHttpServletResponse();
gateway.handleRequest(request, response);
assertThat(response.getStatus(), is(HttpServletResponse.SC_OK));
assertThat(response.getContentType(), is("application/x-java-serialized-object"));
}
}

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:si="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<si:channel id="requests"/>
<outbound-gateway id="minimalConfig" request-channel="requests"/>
<si:channel id="replies">
<si:queue/>
</si:channel>
<outbound-gateway id="fullConfigWithMapper"
request-channel="requests"
request-mapper="mapper"
request-executor="executor"
request-timeout="1234"
reply-channel="replies"
order="77"
auto-startup="false"/>
<outbound-gateway id="fullConfigWithoutMapper"
request-channel="requests"
default-url="http://localhost/test"
extract-request-payload="false"
charset="UTF-8"
request-executor="executor"
request-timeout="1234"
reply-channel="replies"/>
<beans:bean id="mapper" class="org.springframework.integration.http.DefaultOutboundRequestMapper">
<beans:property name="defaultUrl" value="http://localhost/test"/>
<beans:property name="charset" value="UTF-8"/>
<beans:property name="extractPayload" value="false"/>
</beans:bean>
<beans:bean id="executor" class="org.springframework.integration.http.SimpleHttpRequestExecutor"/>
</beans:beans>

View File

@@ -0,0 +1,144 @@
/*
* 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.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.net.URL;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.http.DefaultOutboundRequestMapper;
import org.springframework.integration.http.HttpOutboundEndpoint;
import org.springframework.integration.http.HttpRequestExecutor;
import org.springframework.integration.http.OutboundRequestMapper;
import org.springframework.integration.http.SimpleHttpRequestExecutor;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class HttpOutboundGatewayParserTests {
@Autowired @Qualifier("minimalConfig")
private AbstractEndpoint minimalConfigEndpoint;
@Autowired @Qualifier("fullConfigWithMapper")
private AbstractEndpoint fullConfigWithMapperEndpoint;
@Autowired @Qualifier("fullConfigWithoutMapper")
private AbstractEndpoint fullConfigWithoutMapperEndpoint;
@Autowired
private ApplicationContext applicationContext;
@Test
public void minimalConfig() {
HttpOutboundEndpoint gateway = (HttpOutboundEndpoint) new DirectFieldAccessor(
this.minimalConfigEndpoint).getPropertyValue("handler");
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
this.minimalConfigEndpoint).getPropertyValue("inputChannel");
assertEquals(this.applicationContext.getBean("requests"), requestChannel);
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
Object replyChannel = accessor.getPropertyValue("outputChannel");
assertNull(replyChannel);
OutboundRequestMapper mapper = (OutboundRequestMapper) accessor.getPropertyValue("requestMapper");
HttpRequestExecutor executor = (HttpRequestExecutor) accessor.getPropertyValue("requestExecutor");
assertTrue(mapper instanceof DefaultOutboundRequestMapper);
assertTrue(executor instanceof SimpleHttpRequestExecutor);
Object mapperBean = this.applicationContext.getBean("mapper");
assertNotSame(mapperBean, mapper);
DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(mapper);
assertNull(mapperAccessor.getPropertyValue("defaultUrl"));
assertEquals("UTF-8", mapperAccessor.getPropertyValue("charset"));
assertEquals(true, mapperAccessor.getPropertyValue("extractPayload"));
}
@Test
public void fullConfigWithMapper() throws Exception {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.fullConfigWithMapperEndpoint);
HttpOutboundEndpoint gateway = (HttpOutboundEndpoint) endpointAccessor.getPropertyValue("handler");
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
this.fullConfigWithMapperEndpoint).getPropertyValue("inputChannel");
assertEquals(this.applicationContext.getBean("requests"), requestChannel);
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals(77, accessor.getPropertyValue("order"));
assertEquals(Boolean.FALSE, endpointAccessor.getPropertyValue("autoStartup"));
Object replyChannel = accessor.getPropertyValue("outputChannel");
assertNotNull(replyChannel);
assertEquals(this.applicationContext.getBean("replies"), replyChannel);
OutboundRequestMapper mapper = (OutboundRequestMapper) accessor.getPropertyValue("requestMapper");
HttpRequestExecutor executor = (HttpRequestExecutor) accessor.getPropertyValue("requestExecutor");
assertTrue(mapper instanceof DefaultOutboundRequestMapper);
assertTrue(executor instanceof SimpleHttpRequestExecutor);
Object mapperBean = this.applicationContext.getBean("mapper");
assertEquals(mapperBean, mapper);
DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(mapper);
assertEquals(new URL("http://localhost/test"), mapperAccessor.getPropertyValue("defaultUrl"));
assertEquals("UTF-8", mapperAccessor.getPropertyValue("charset"));
assertEquals(false, mapperAccessor.getPropertyValue("extractPayload"));
Object executorBean = this.applicationContext.getBean("executor");
assertEquals(executorBean, executor);
Object sendTimeout = new DirectFieldAccessor(
accessor.getPropertyValue("channelTemplate")).getPropertyValue("sendTimeout");
assertEquals(new Long("1234"), sendTimeout);
}
@Test
public void fullConfigWithoutMapper() throws Exception {
HttpOutboundEndpoint gateway = (HttpOutboundEndpoint) new DirectFieldAccessor(
this.fullConfigWithoutMapperEndpoint).getPropertyValue("handler");
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
this.fullConfigWithoutMapperEndpoint).getPropertyValue("inputChannel");
assertEquals(this.applicationContext.getBean("requests"), requestChannel);
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
Object replyChannel = accessor.getPropertyValue("outputChannel");
assertNotNull(replyChannel);
assertEquals(this.applicationContext.getBean("replies"), replyChannel);
OutboundRequestMapper mapper = (OutboundRequestMapper) accessor.getPropertyValue("requestMapper");
HttpRequestExecutor executor = (HttpRequestExecutor) accessor.getPropertyValue("requestExecutor");
assertTrue(mapper instanceof DefaultOutboundRequestMapper);
assertTrue(executor instanceof SimpleHttpRequestExecutor);
Object mapperBean = this.applicationContext.getBean("mapper");
assertNotSame(mapperBean, mapper);
DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(mapper);
assertEquals(new URL("http://localhost/test"), mapperAccessor.getPropertyValue("defaultUrl"));
assertEquals("UTF-8", mapperAccessor.getPropertyValue("charset"));
assertEquals(false, mapperAccessor.getPropertyValue("extractPayload"));
Object executorBean = this.applicationContext.getBean("executor");
assertEquals(executorBean, executor);
Object sendTimeout = new DirectFieldAccessor(
accessor.getPropertyValue("channelTemplate")).getPropertyValue("sendTimeout");
assertEquals(new Long("1234"), sendTimeout);
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.mapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.integration.core.Message;
import org.springframework.integration.http.DataBindingInboundRequestMapper;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
/**
* @author Mark Fisher
*/
public class DataBindingInboundRequestMapperTests {
@Test
public void bindToType() throws Exception {
DataBindingInboundRequestMapper mapper = new DataBindingInboundRequestMapper(TestBean.class);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name", "testBean");
request.setParameter("age", "42");
Message<?> result = mapper.toMessage(request);
assertNotNull(result);
assertEquals(TestBean.class, result.getPayload().getClass());
TestBean payload = (TestBean) result.getPayload();
assertEquals("testBean", payload.name);
assertEquals(84, payload.age);
}
@Test
public void bindToTypeWithBindingInitializer() throws Exception {
DataBindingInboundRequestMapper mapper = new DataBindingInboundRequestMapper(TestBean.class);
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setDirectFieldAccess(true);
mapper.setWebBindingInitializer(initializer);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name", "testBean");
request.setParameter("age", "42");
Message<?> result = mapper.toMessage(request);
assertNotNull(result);
assertEquals(TestBean.class, result.getPayload().getClass());
TestBean payload = (TestBean) result.getPayload();
assertEquals("testBean", payload.name);
assertEquals(42, payload.age);
}
@Test
public void bindToPrototypeBean() throws Exception {
StaticApplicationContext context = new StaticApplicationContext();
MutablePropertyValues properties = new MutablePropertyValues();
properties.addPropertyValue("name", "prototype");
context.registerPrototype("prototypeTarget", TestBean.class, properties);
DataBindingInboundRequestMapper mapper = new DataBindingInboundRequestMapper(TestBean.class);
mapper.setTargetBeanName("prototypeTarget");
mapper.setBeanFactory(context);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("age", "42");
Message<?> result = mapper.toMessage(request);
assertNotNull(result);
assertEquals(TestBean.class, result.getPayload().getClass());
TestBean payload = (TestBean) result.getPayload();
assertEquals("prototype", payload.name);
assertEquals(84, payload.age);
}
public static class TestBean {
String name;
int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age * 2;
}
}
}