HttpRestRouter.java

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2006, JBoss Inc., and others contributors as indicated
 * by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License, v. 2.1.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License,
 * v.2.1 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 * (C) 2005-2006, JBoss Inc.
 */
package cl.mas.siniestros.esb.action.ejemplo;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.publish.Publish;
import org.jboss.internal.soa.esb.util.StreamUtils;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.actions.ActionLifecycleException;
import org.jboss.soa.esb.actions.ActionProcessingException;
import org.jboss.soa.esb.actions.annotation.Process;
import org.jboss.soa.esb.actions.routing.AbstractRouter;
import org.jboss.soa.esb.actions.routing.http.HttpMethodFactory;
import org.jboss.soa.esb.actions.routing.http.ResponseType;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.http.HttpClientFactory;
import org.jboss.soa.esb.http.HttpHeader;
import org.jboss.soa.esb.http.HttpRequest;
import org.jboss.soa.esb.http.HttpResponse;
import org.jboss.soa.esb.listeners.ListenerTagNames;
import org.jboss.soa.esb.listeners.message.MessageDeliverException;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.Properties;
import org.jboss.soa.esb.message.ResponseHeader;
import org.jboss.soa.esb.message.ResponseStatus;
import org.jboss.soa.esb.message.format.MessageFactory;
import org.jboss.soa.esb.util.FileUtil;

public class HttpRestRouter extends AbstractRouter {

    private static Logger logger = Logger.getLogger(HttpRestRouter.class);

    private ConfigTree config;
    private java.util.Properties httpClientProps = new java.util.Properties();
    private HttpClient httpclient;
    private URL endpointUrl;
    private String method;
    private HttpMethodFactory methodFactory;
    private ResponseType responseType;
    private String contentType;
    private ConfigTree[] requestHeaders;
    private boolean httpResponseStatusEnabled;
    private String rsPath = "";
    private String rsUrl;

    private final String[] mappedHeaderList ;

    private static Set<String> coreProperties;
    static {
        coreProperties = new HashSet<String>();
        coreProperties.add("endpointUrl");
        coreProperties.add("method");
        coreProperties.add("responseType");
        coreProperties.add("Content-Type");
        coreProperties.add("method");
    }



    public HttpRestRouter(ConfigTree config) throws ConfigurationException {
        super(config);
        this.config = config;

           rsUrl = config.getRequiredAttribute("endpointUrl");
        this.method = config.getRequiredAttribute("method");
        this.rsPath = config.getRequiredAttribute("path");
        String substring = this.rsPath.substring(0, 1);
        if("/".equals(substring))
            this.rsPath = this.rsPath.replaceFirst("/", "");   
        extractHttpClientProps(config);
        mappedHeaderList = extractMappedHeaderListConfig();
    }

    public void init(String url, String contentType)throws ConfigurationException {

        this.httpclient = HttpClientFactory.createHttpClient(httpClientProps);

        try {
            this.endpointUrl = new URL(url);
        } catch (MalformedURLException e) {
            throw new ConfigurationException("Invalid endpoint URL '" + config.getRequiredAttribute("endpointUrl") + "'.", e);
        }
        this.responseType = ResponseType.valueOf(config.getAttribute("responseType", ResponseType.STRING.toString()));
        this.methodFactory = HttpMethodFactory.Factory.getInstance(method.toUpperCase(), config, endpointUrl);
        this.contentType = config.getAttribute("Content-Type");

        this.requestHeaders = config.getChildren("header");

        this.httpResponseStatusEnabled = ResponseStatus.isHttpEnabled(config);
    }

    private String formURL(HttpRequest request) throws ActionProcessingException{
        String auxPath = "";
        String requestURI = request.getRequestURI();
        String esbUrl = request.getContextPath()+ request.getRequestPath();        
        String restURI = requestURI.replace(esbUrl, "");
        String subString = "";
        if(restURI != null && restURI.length() > 0)
            subString = restURI.substring(0, 1);
        if("/".equals(subString))
            restURI = restURI.replaceFirst("/", "");
        String[] listRsPath = this.rsPath.split("/");
        String[] listRsURI = restURI.split("/");

        if(listRsPath == null)
            throw new ActionProcessingException("listRSPath can not be null");

        if(listRsURI == null)
            throw new ActionProcessingException("listRsURI can not be null");

        if(listRsURI.length != listRsPath.length)
            throw new ActionProcessingException("listRSPath not equal listRsURI");


        if(listRsPath.length != 0){
            String[] token = new String[listRsPath.length];
            for (int i = 0; i < listRsPath.length; i++) {
                if(listRsPath[i].startsWith("{")){
                    token[i] = listRsURI[i];
                }else{
                    if(!listRsPath[i].equals(listRsURI[i]))
                        throw new ActionProcessingException("listRSPath not equal listRsURI");
                    token[i] = listRsPath[i];
                }
            }
            auxPath = this.implode(token, "/");
        }
        auxPath = this.rsUrl  +"/"+ auxPath;

        return auxPath;
    }

     public static String implode(String[] ary, String delim) {
          String out = "";
           for(int i=0; i<ary.length; i++) {
               if(i!=0) { out += delim; }
               out += ary[i];
          }
     }

    @Process
    public Message process(Message message) throws ActionProcessingException {
        HttpMethodBase httpMethod = null;
        HttpRequest request = HttpRequest.getRequest(message);


        String path = this.formURL(request);      
        try {
            this.init(path, request.getContentType());
        } catch (ConfigurationException e1) {
            e1.printStackTrace();
            throw new ActionProcessingException(e1);
        }
        try {
            httpMethod = methodFactory.getInstance(message);
            httpMethod.setQueryString(request.getQueryString());           

            try {
                setRequestHeaders(httpMethod, message);

                int responseCode = httpclient.executeMethod(httpMethod);
                if(responseCode != HttpStatus.SC_OK) {
                    logger.warn("Received status code '" + responseCode + "' on HTTP " + httpMethod + " request to '" + endpointUrl + "'.");
                }
                attachResponseDetails(message, httpMethod, responseCode);

                InputStream resultStream = httpMethod.getResponseBodyAsStream();
                try {
                    byte[] bytes = readStream(resultStream);

                    if(responseType == ResponseType.STRING) {
                        getPayloadProxy().setPayload(message, new String(bytes, httpMethod.getResponseCharSet()));
                    } else {
                        getPayloadProxy().setPayload(message, bytes);
                    }
                } catch (MessageDeliverException e) {
                    throw new ActionProcessingException("problem setting message payload: " + e.getMessage(), e);
                } finally {
                    closeStream(resultStream);
                }
            } finally {
                httpMethod.releaseConnection();
            }
        } catch (IOException e) {
            throw new ActionProcessingException("problem processing HTTP I/O: " + e.getMessage(), e);
        }

        return message;
    }

    static byte[] readStream(final InputStream stream) throws IOException {
        if (stream != null) {
            return StreamUtils.readStream(stream);
        }
        else
           return new byte[0];
    }

    static void closeStream(final Closeable c) throws IOException {
        if (c != null) {
            c.close();
        }
    }

    private String[] extractMappedHeaderListConfig() throws ConfigurationException {
        final String mappedHeaders = config.getAttribute("MappedHeaderList");
        if (mappedHeaders != null) {
            final String[] headerList = mappedHeaders.split(",");
            final int numHeaders = headerList.length ;
            final ArrayList<String> headers = new ArrayList<String>(numHeaders) ;

            for(int count = 0 ; count < numHeaders ; count++) {
                final String header = headerList[count].trim() ;
                if (header.length() > 0) {
                    headers.add(header) ;
                }
            }
            return (String[])headers.toArray(new String[headers.size()]) ;
        } else {
            return new String[0] ;
        }
    }

    private void attachResponseDetails(Message message, HttpMethodBase method, int responseCode) {
        HttpResponse response = new HttpResponse(responseCode);
        Properties properties = message.getProperties();

        response.setEncoding(method.getResponseCharSet());
        response.setLength(method.getResponseContentLength());

        Header[] responseHeaders = method.getResponseHeaders();
        for(Header responseHeader : responseHeaders) {
          String name = responseHeader.getName();
          String value = responseHeader.getValue();
            response.addHeader(new org.jboss.soa.esb.http.HttpHeader(name, value));
            // JBESB-2511
            new ResponseHeader(name, value).setPropertyNameThis(properties);
        }
        // JBESB-2761
        if (httpResponseStatusEnabled) {
          ResponseStatus.setHttpProperties(properties, responseCode, method.getStatusLine().getReasonPhrase());
        }

        response.setResponse(message);
    }

    private void setRequestHeaders(HttpMethodBase method, Message message) {
        setMappedHttpHeaders(method, message);        
        for (int i = 0; i < requestHeaders.length; i++) {
            ConfigTree header = requestHeaders[i];
            String name = header.getAttribute("name");
            String value = header.getAttribute("value");

            if(name != null && value != null) {
                method.setRequestHeader(name, value);
            } else {
                logger.error("null Http request header name/value: '" + name + "':'" + value + "'.");
            }
        }
        if (contentType != null) {
            method.setRequestHeader("Content-Type", contentType);
        } else if (method.getRequestHeader("Content-Type") == null) {
            method.setRequestHeader("Content-Type", "text/xml;charset=UTF-8") ;
        }
    }

  private void setMappedHttpHeaders(HttpMethodBase method, Message message) {
      HttpRequest request = HttpRequest.getRequest(message);
      Properties properties = message.getProperties();
      for (String headerName : mappedHeaderList) {
        String headerValue = null;
        if (request != null) {
          headerValue = getHttpHeaderValue(request, headerName);
        }
        if (headerValue == null) {
          headerValue = getHttpHeaderValue(properties, headerName);
        }
        if (headerValue != null) {
          method.setRequestHeader(headerName, headerValue);
        }
      }
  }

    private String getHttpHeaderValue(HttpRequest request, String headerName) {
      String headerValue = null;
        for (HttpHeader header : request.getHeaders()) {
          String name = header.getName();
            if (name.equalsIgnoreCase(headerName)) {
              headerValue = header.getValue();
              break;
            }
        }
        return headerValue;
    }

    private String getHttpHeaderValue(Properties properties, String headerName) {
      String headerValue = null;
        for (String name : properties.getNames()) {
            if (name.equalsIgnoreCase(headerName)) {
              Object property = properties.getProperty(name);
              if (property != null) {
                headerValue = property.toString();
                if (headerValue.length() == 0) {
                  headerValue = null;
                }
              }
              break;
            }
        }
        return headerValue;
    }

    public String[] getMappedHeaderList() {
        return mappedHeaderList;
    }

    public void destroy() throws ActionLifecycleException {
        if (httpclient != null) {
            HttpClientFactory.shutdown(httpclient);
        }
        super.destroy();
    }

    private void extractHttpClientProps(ConfigTree config) {
        ConfigTree[] httpClientConfigTrees = config.getChildren("http-client-property");

        httpClientProps.setProperty(HttpClientFactory.TARGET_HOST_URL, this.rsUrl);
        final ConfigTree parent = config.getParent() ;
        if (parent != null) {
            final String maxThreads = config.getParent().getAttribute(ListenerTagNames.MAX_THREADS_TAG) ;
            if (maxThreads != null) {
                httpClientProps.setProperty("3", maxThreads) ;
            }
        }

        for(ConfigTree httpClientProp : httpClientConfigTrees) {
            String propName = httpClientProp.getAttribute("name");
            String propValue = httpClientProp.getAttribute("value");

            if(propName != null && propValue != null) {
                httpClientProps.setProperty(propName, propValue);
            }
        }
    }

    public static void main(String[] args) throws ConfigurationException, ActionProcessingException {
        ConfigTree configTree = new ConfigTree("config");

        for(String arg : args) {
            int equalsIdx = arg.indexOf('=');

            if(equalsIdx == -1) {
                throw new IllegalArgumentException("Arguments must be in 'name=value' format.");
            }

            String name = arg.substring(0, equalsIdx);
            String value = arg.substring(equalsIdx + 1);
            if(!coreProperties.contains(name) && !name.equals("payload")) {
                ConfigTree httpClientProperty = new ConfigTree("http-client-property", configTree);
                httpClientProperty.setAttribute("name", name);
                httpClientProperty.setAttribute("value", value);
            } else {
                configTree.setAttribute(name, value);
            }
        }

        HttpRestRouter router = new HttpRestRouter(configTree);
        Message message = MessageFactory.getInstance().getMessage();

        String payload = configTree.getAttribute("payload");
        if(payload != null) {
            try {
                File file = new File(payload);
                if(file.exists()) {
                    payload = FileUtil.readTextFile(file);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("Request payload:\n" + payload);
            System.out.println("--------------------------\n");
            message.getBody().add(payload);
        }

        message = router.process(message);

        HttpResponse responseInfo = HttpResponse.getResponse(message);
        System.out.println();
        System.out.println("Response Status Code: " + responseInfo.getResponseCode());
        System.out.println("Response payload:\n" + message.getBody().get());
        System.out.println("--------------------------\n");
    }

    @Override
    public void route(Object arg0) throws ActionProcessingException {
        // TODO Auto-generated method stub

    }
}