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(); } }
<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 のご紹介 | オブジェクトの広場