RestProxyをリファクタリング+機能追加(修正)してみた

ここで言及されているJBossESBにおけるRestProxyActionをリファクタリング+機能追加(修正)してみた。
httprouter issues with GET/POST WADL Services a... |JBoss Developer


<機能追加(修正)>

  • HTTP-POSTのパラメータが正しく処理できないバグを修正
  • レスポンスにContent-Typeが正しく設定されないバグを修正(そのためブラウザによってはHTMLとして処理されない)
  • 画像が処理出来ないバグを修正
  • リダイレクト(HTTPコード:302)に対応する機能を追加
  • 2度目以降のアクセスもESBを通るように、コンテキストパスを書き換える機能を追加

リファクタリング

  • DRY原則に則って重複コードを統合


◆RestProxyAction

package org.sample.soa.esb.actions;

import static org.sample.soa.esb.common.utils.Util.isEmpty;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.jboss.soa.esb.actions.ActionProcessingException;
import org.jboss.soa.esb.actions.annotation.Process;
import org.jboss.soa.esb.configure.ConfigProperty;
import org.jboss.soa.esb.http.HttpHeader;
import org.jboss.soa.esb.http.HttpRequest;
import org.jboss.soa.esb.message.Message;

public class RestProxyAction {

	private static final Logger log = Logger.getLogger(RestProxyAction.class);

	@ConfigProperty
	private String endpointUrl;

	@Process
	public Message process(Message message) throws ActionProcessingException {

		HttpRequest request = HttpRequest.getRequest(message);

		String endpoint = buildEndpointUrl(request);

		String inputXml = getStringPayload(message);

		HashMap<String, String> headers = getHeaders(request.getHeaders());

		HashMap<String, String> params = getParams(request.getQueryParams());

		HttpClientUtil.execute(message, request.getMethod(), inputXml,
				endpoint, params, headers);

		return message;
	}

	private String buildEndpointUrl(HttpRequest request) {
		String endpoint = endpointUrl;
		String pathInfo = request.getPathInfo();
		if (pathInfo != null && !pathInfo.isEmpty()) {
			endpoint += pathInfo.substring(1);
		}
		return endpoint;
	}

	private String getStringPayload(Message message) {

		String inputXml = null;
		Object payload = message.getBody().get();
		if (payload != null && !(payload instanceof byte[])) {
			inputXml = (String) payload;
		}
		return inputXml;
	}

	private HashMap<String, String> getHeaders(List<HttpHeader> requestHeaders) {

		if (isEmpty(requestHeaders))
			return null;

		log.debug("****************** Request Headers ******************");

		HashMap<String, String> headers = new HashMap<String, String>();
		for (HttpHeader header : requestHeaders) {
			log.info("key: " + header.getName() + " " + "value: "
					+ header.getValue());
			headers.put(header.getName(), header.getValue());
		}
		return headers;
	}

	private HashMap<String, String> getParams(
			Map<String, String[]> requestParameters) {

		if (isEmpty(requestParameters))
			return null;

		log.info("******************Request parameters ******************");

		HashMap<String, String> params = new HashMap<String, String>();
		for (String s : requestParameters.keySet()) {
			String[] value = requestParameters.get(s);
			log.info("key " + s + " " + "value " + value[0]);
			params.put(s, value[0]);
		}
		return params;
	}

}

◆HttpClientUtil

package org.sample.soa.esb.actions;

import static org.sample.soa.esb.common.utils.Util.isEmpty;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.NameValuePair;
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.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.jboss.soa.esb.http.HttpHeader;
import org.jboss.soa.esb.http.HttpResponse;
import org.jboss.soa.esb.message.Message;

public class HttpClientUtil {

	public static final Logger log = Logger.getLogger(HttpClientUtil.class);

	public static void execute(Message message, String methodName, String xml,
			String url, HashMap<String, String> params,
			HashMap<String, String> headers) {

		log.info("URL >> " + url);

		HttpMethodBase method = MethodFactory.getInstance().getMethod(
				methodName, url);
		try {
			if (!isEmpty(params))
				method.setQueryString(buildQueryParameters(params));

			if (!isEmpty(headers))
				setRequestHeaders(method, headers);

			if (method instanceof EntityEnclosingMethod) {
				// case: method is POST or PUT
				StringRequestEntity entity = new StringRequestEntity(xml,
						"application/xml", "UTF8");
				((EntityEnclosingMethod) method).setRequestEntity(entity);
			}

			int httpStatusCode = new HttpClient().executeMethod(method);
			log.info("HTTP Status code is >> " + httpStatusCode);

			setHttpResponse(message, method, httpStatusCode);

			if ("GET".equals(methodName)) {
				String contentType = method.getResponseHeader("Content-Type")
						.getValue();
				if (contentType != null && contentType.indexOf("image/") != -1)
					message.getBody().add(method.getResponseBody());
				else
					setStringPayload(message, method);

			} else {
				setStringPayload(message, method);
			}

		} catch (Exception e) {
			log.error(e);

		} finally {
			method.releaseConnection();
		}
	}

	private static void setStringPayload(Message message, HttpMethodBase method)
			throws IOException, UnsupportedEncodingException {
		InputStream response = method.getResponseBodyAsStream();
		message.getBody().add(
				new String(IOUtils.toByteArray(response), method
						.getResponseCharSet()));
	}

	private static void setHttpResponse(Message message, HttpMethodBase method,
			int httpStatusCode) {
		HttpResponse response = new HttpResponse(httpStatusCode);
		response.setEncoding(method.getResponseCharSet());
		for (Header h : method.getResponseHeaders()) {
			HttpHeader header = new HttpHeader(h.getName(), h.getValue());
			response.addHeader(header);
		}
		response.setResponse(message);
	}

	private static void setRequestHeaders(HttpMethod method,
			HashMap<String, String> headers) {

		if (isEmpty(headers))
			return;

		log.info(" ************** headers ************** ");

		for (String s : headers.keySet()) {
			log.info("header=" + s + " valkue=" + headers.get(s));
			method.setRequestHeader(s, headers.get(s));
		}
	}

	private static NameValuePair[] buildQueryParameters(
			HashMap<String, String> parameters) throws Exception {

		log.info(" ************** parameters ************** ");

		NameValuePair[] ret = new NameValuePair[parameters.size()];

		int index = 0;
		for (String key : parameters.keySet()) {
			log.info("parameter=" + key + " value=" + parameters.get(key));
			ret[index] = new NameValuePair(key,
					URIUtil.encodeQuery(parameters.get(key)));
			index++;
		}
		return ret;
	}

	private static class MethodFactory {

		private static MethodFactory thisInstance = new MethodFactory();

		private MethodFactory() {
		}

		public static MethodFactory getInstance() {
			return thisInstance;
		}

		public HttpMethodBase getMethod(String method, String url) {

			if ("GET".equals(method)) {
				return new GetMethod(url);

			} else if ("POST".equals(method)) {
				return new PostMethod(url);

			} else if ("PUT".equals(method)) {
				return new PutMethod(url);

			} else if ("DELETE".equals(method)) {
				return new DeleteMethod(url);

			} else {
				return null;
			}
		}
	}

}

◆ConvertUrlAction

package org.sample.soa.esb.actions;

import java.net.MalformedURLException;
import java.net.URL;

import org.jboss.soa.esb.actions.annotation.Process;
import org.jboss.soa.esb.configure.ConfigProperty;
import org.jboss.soa.esb.http.HttpHeader;
import org.jboss.soa.esb.http.HttpResponse;
import org.jboss.soa.esb.lifecycle.annotation.Initialize;
import org.jboss.soa.esb.message.Message;

public class ConvertUrlAction {

	@ConfigProperty
	private URL endpointUrl;

	@ConfigProperty
	private String convertPath;

	@ConfigProperty
	private String convertLocation;

	private String targetPath;

	private String targetLocation;

	@Initialize
	public void initialize() throws MalformedURLException {
		targetPath = endpointUrl.getPath();
		targetLocation = endpointUrl.getHost();
		if (endpointUrl.getPort() != -1) {
			targetLocation += ":" + endpointUrl.getPort();
		}
	}

	@Process
	public Message process(Message message) {
		convertContextPath(message);
		convertRedirectLocation(message);
		return message;
	}

	private void convertContextPath(Message message) {
		Object payload = message.getBody().get();
		if (payload instanceof String) {
			payload = ((String) payload).replaceAll(targetPath, convertPath);
			message.getBody().add(payload);
		}
	}

	private void convertRedirectLocation(Message message) {
		
		HttpResponse response = HttpResponse.getResponse(message);
		if (response == null || response.getResponseCode() != 302) {
			return;
		}

		for (HttpHeader h : response.getHttpHeaders()) {
			String name = h.getName();
			if ("Location".equals(name)) {
				String tmp = h.getValue().replaceAll(
						targetLocation + targetPath,
						convertLocation + convertPath);
				response.getHttpHeaders().remove(h);
				HttpHeader header = new HttpHeader(name, tmp);
				response.addHeader(header);
				break;
			}
		}
		response.setResponse(message);
	}
}

◆Util

package org.sample.soa.esb.common.utils;

import java.util.List;
import java.util.Map;

public class Util {

	public static boolean isEmpty(Map<?, ?> map) {
		return (map == null) || map.isEmpty();
	}
	
	public static boolean isEmpty(List<?> list){
		return (list == null) || list.isEmpty();
	}

}

jboss-esb.xml

<service category="RestProxyService" description="" invmScope="GLOBAL"
	name="Rest">
	<listeners>
		<http-gateway name="rest_gw" payloadAs="STRING"
			urlPattern="rest/*">
			<property name="synchronousTimeout" value="150000" />
		</http-gateway>
	</listeners>
	<actions mep="RequestResponse">
		<action class="org.sample.soa.esb.actions.RestProxyAction"
			name="RestProxyAction">
			<property name="endpointUrl" value="http://localhost:8999/rest-sample/" />
		</action>
		<action class="org.sample.soa.esb.actions.ConvertUrlAction"
			name="ConvertUrlAction">
			<property name="endpointUrl" value="http://localhost:8999/rest-sample/" />
			<property name="convertPath" value="/sample-esb/http/rest/" />
			<property name="convertLocation" value="localhost:8080" />
		</action>
	</actions>
</service>

これでだいぶすっきりした。



なお、今回の対向のRESTサービスはSprint Rooにて生成した。
Spring Rooについてはこちらを参考に。
Spring Roo のご紹介 | オブジェクトの広場