All Downloads are FREE. Search and download functionalities are using the official Maven repository.

smartrics.rest.client.RestClientImpl Maven / Gradle / Ivy

/*  Copyright 2008 Fabrizio Cannizzo
 *
 *  This file is part of RestFixture.
 *
 *  RestFixture (http://code.google.com/p/rest-fixture/) is free software:
 *  you can redistribute it and/or modify it under the terms of the
 *  GNU Lesser General Public License as published by the Free Software Foundation,
 *  either version 3 of the License, or (at your option) any later version.
 *
 *  RestFixture is distributed in the hope that it will be useful,
 *  but WITHOUT ANY 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
 *  along with RestFixture.  If not, see .
 *
 *  If you want to contact the author please leave a comment here
 *  http://smartrics.blogspot.com/2008/08/get-fitnesse-with-some-rest.html
 */
package smartrics.rest.client;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

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.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.FileRequestEntity;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A generic REST client based on {@code HttpClient}.
 */
public class RestClientImpl implements RestClient {

    private static Logger LOG = LoggerFactory.getLogger(RestClientImpl.class);

    private final HttpClient client;

    private String baseUrl;

    /**
     * Constructor allowing the injection of an {@code
     * org.apache.commons.httpclient.HttpClient}.
     *
     * @param client the client
     *               See {@link org.apache.commons.httpclient.HttpClient}
     */
    public RestClientImpl(HttpClient client) {
        if (client == null)
            throw new IllegalArgumentException("Null HttpClient instance");
        this.client = client;
    }

    /**
     * See {@link smartrics.rest.client.RestClient#setBaseUrl(java.lang.String)}
     */
    public void setBaseUrl(String bUrl) {
        this.baseUrl = bUrl;
    }

    /**
     * See {@link smartrics.rest.client.RestClient#getBaseUrl()}
     */
    public String getBaseUrl() {
        return baseUrl;
    }

    /**
     * Returns the Http client instance used by this implementation.
     *
     * @return the instance of HttpClient
     * See {@link org.apache.commons.httpclient.HttpClient}
     * See {@link smartrics.rest.client.RestClientImpl#RestClientImpl(HttpClient)}
     */
    public HttpClient getClient() {
        return client;
    }

    /**
     * See {@link smartrics.rest.client.RestClient#execute(smartrics.rest.client.RestRequest)}
     */
    public RestResponse execute(RestRequest request) {
        return execute(getBaseUrl(), request);
    }

    /**
     * See {@link smartrics.rest.client.RestClient#execute(java.lang.String, smartrics.rest.client.RestRequest)}
     */
    public RestResponse execute(String hostAddr, final RestRequest request) {
        if (request == null || !request.isValid())
            throw new IllegalArgumentException("Invalid request " + request);
        if (request.getTransactionId() == null)
            request.setTransactionId(Long.valueOf(System.currentTimeMillis()));
        LOG.debug("request: {}", request);
        HttpMethod m = createHttpClientMethod(request);
        configureHttpMethod(m, hostAddr, request);
        // Debug Client
        if (LOG.isDebugEnabled()) {
            try {
                LOG.info("Http Request URI : {}", m.getURI());
            } catch (URIException e) {
                LOG.error("Error URIException in debug : " + e.getMessage(), e);
            }
            // Request Header
            LOG.debug("Http Request Method Class : {} ",    m.getClass()  );
            LOG.debug("Http Request Header : {} ",    Arrays.toString( m.getRequestHeaders()) );
            // Request Body
            if (m instanceof EntityEnclosingMethod) {
                try {
                    ByteArrayOutputStream requestOut = new ByteArrayOutputStream();
                    ((EntityEnclosingMethod) m).getRequestEntity().writeRequest(requestOut);
                    LOG.debug("Http Request Body : {}", requestOut.toString());
                } catch (IOException e) {
                    LOG.error("Error in reading request body in debug : " + e.getMessage(), e);
                }
            }
        }
        // Prepare Response
        RestResponse resp = new RestResponse();
        resp.setTransactionId(request.getTransactionId());
        resp.setResource(request.getResource());
        try {
            client.executeMethod(m);
            for (Header h : m.getResponseHeaders()) {
                resp.addHeader(h.getName(), h.getValue());
            }
            resp.setStatusCode(m.getStatusCode());
            resp.setStatusText(m.getStatusText());
            resp.setRawBody(m.getResponseBody());
            // Debug
            if (LOG.isDebugEnabled()) {
                LOG.debug("Http Request Path : {}", m.getPath());
                LOG.debug("Http Request Header : {} ", Arrays.toString( m.getRequestHeaders()) );
                LOG.debug("Http Response Status : {}", m.getStatusLine() );
                LOG.debug("Http Response Body : {}", m.getResponseBodyAsString() );
            }

        } catch (HttpException e) {
            String message = "Http call failed for protocol failure";
            throw new IllegalStateException(message, e);
        } catch (IOException e) {
            String message = "Http call failed for IO failure";
            throw new IllegalStateException(message, e);
        } finally {
            m.releaseConnection();
        }
        LOG.debug("response: {}", resp);
        return resp;
    }

    /**
     * Configures the instance of HttpMethod with the data in the request and
     * the host address.
     *
     * @param m        the method class to configure
     * @param hostAddr the host address
     * @param request  the rest request
     */
    protected void configureHttpMethod(HttpMethod m, String hostAddr, final RestRequest request) {
        addHeaders(m, request);
        setUri(m, hostAddr, request);
        m.setQueryString(request.getQuery());
        if (m instanceof EntityEnclosingMethod) {
            RequestEntity requestEntity = null;
            String fileName = request.getFileName();
            if (fileName != null) {
                requestEntity = configureFileUpload(fileName);
            } else {
                // Add Multipart
                Map multipartFiles = request.getMultipartFileNames();
                if ((multipartFiles != null) && (!multipartFiles.isEmpty())) {
                    requestEntity = configureMultipartFileUpload(m, request, requestEntity, multipartFiles);
                } else {
                    requestEntity = new RequestEntity() {
                        public boolean isRepeatable() {
                            return true;
                        }

                        public void writeRequest(OutputStream out) throws IOException {
                            PrintWriter printer = new PrintWriter(out);
                            printer.print(request.getBody());
                            printer.flush();
                        }

                        public long getContentLength() {
                            return request.getBody().getBytes().length;
                        }

                        public String getContentType() {
                            List values = request.getHeader("Content-Type");
                            String v = "text/xml";
                            if (values.size() != 0)
                                v = values.get(0).getValue();
                            return v;
                        }
                    };
                }
            }
            ((EntityEnclosingMethod) m).setRequestEntity(requestEntity);
        } else {
            m.setFollowRedirects(request.isFollowRedirect());
        }

    }


    private RequestEntity configureMultipartFileUpload(HttpMethod m, final RestRequest request, RequestEntity requestEntity, Map multipartFiles) {
        MultipartRequestEntity multipartRequestEntity = null;
        // Current File Name reading for tracking missing file
        String fileName = null;

        List fileParts = new ArrayList(multipartFiles.size());
        // Read File Part
        for (Map.Entry multipartFile : multipartFiles.entrySet()) {
            Part filePart = createMultipart(multipartFile.getKey(), multipartFile.getValue());
            fileParts.add(filePart);
        }
        Part[] parts = fileParts.toArray(new Part[fileParts.size()]);
        multipartRequestEntity = new MultipartRequestEntity(parts, ((EntityEnclosingMethod) m).getParams());

        return multipartRequestEntity;
    }

    private Part createMultipart(String fileParamName, RestMultipart restMultipart) {
        RestMultipart.RestMultipartType type = restMultipart.getType();
        switch (type) {
            case FILE:
                String fileName = null;
                try {
                    fileName = restMultipart.getValue();
                    File file = new File(fileName);
                    FilePart filePart = new FilePart(fileParamName, file, restMultipart.getContentType(), restMultipart.getCharset());
                    LOG.info("Configure Multipart file upload paramName={} :  ContentType={} for  file={} ", new String[]{ fileParamName,  restMultipart.getContentType(), fileName});
                    return filePart;
                } catch (FileNotFoundException e) {
                    throw new IllegalArgumentException("File not found: " + fileName, e);
                }
            case STRING:
                StringPart stringPart = new StringPart(fileParamName, restMultipart.getValue(), restMultipart.getCharset());
                stringPart.setContentType(restMultipart.getContentType());
                LOG.info("Configure Multipart String upload paramName={} :  ContentType={} ", fileParamName, stringPart.getContentType());
                return stringPart;
            default:
                throw new IllegalArgumentException("Unknonw Multipart Type : " + type);
        }

    }



    private RequestEntity configureFileUpload(String fileName) {
        final File file = new File(fileName);
        if (!file.exists()) {
            throw new IllegalArgumentException("File not found: " + fileName);
        }
        return new FileRequestEntity(file, "application/octet-stream");
    }

    public String getContentType(RestRequest request) {
        List values = request.getHeader("Content-Type");
        String v = "text/xml";
        if (values.size() != 0)
            v = values.get(0).getValue();
        return v;
    }

    private void setUri(HttpMethod m, String hostAddr, RestRequest request) {
        String host = hostAddr == null ? client.getHostConfiguration().getHost() : hostAddr;
        if (host == null)
            throw new IllegalStateException("hostAddress is null: please config httpClient host configuration or " + "pass a valid host address or config a baseUrl on this client");
        String uriString = host + request.getResource();
        boolean escaped = request.isResourceUriEscaped();
        try {
            m.setURI(createUri(uriString, escaped));
        } catch (URIException e) {
            throw new IllegalStateException("Problem when building URI: " + uriString, e);
        } catch (NullPointerException e) {
            throw new IllegalStateException("Building URI with null string", e);
        }
    }

    protected URI createUri(String uriString, boolean escaped) throws URIException {
        return new URI(uriString, escaped);
    }

    /**
     * factory method that maps a string with a HTTP method name to an
     * implementation class in Apache HttpClient. Currently the name is mapped
     * to org.apache.commons.httpclient.methods.%sMethod where
     * %s is the parameter mName.
     *
     * @param mName the method name
     * @return the method class
     */
    protected String getMethodClassnameFromMethodName(String mName) {
        return String.format("org.apache.commons.httpclient.methods.%sMethod", mName);
    }

    /**
     * Utility method that creates an instance of {@code
     * org.apache.commons.httpclient.HttpMethod}.
     *
     * @param request the rest request
     * @return the instance of {@code org.apache.commons.httpclient.HttpMethod}
     * matching the method in RestRequest.
     */
    @SuppressWarnings("unchecked")
    protected HttpMethod createHttpClientMethod(RestRequest request) {
        String mName = request.getMethod().toString();
        String className = getMethodClassnameFromMethodName(mName);
        try {
            Class clazz = (Class) Class.forName(className);
            if (className.endsWith("TraceMethod")) {
                return clazz.getConstructor(String.class).newInstance("http://dummy.com");
            } else {
                return clazz.newInstance();
            }
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(className + " not found: you may be using a too old or " + "too new version of HttpClient", e);
        } catch (InstantiationException e) {
            throw new IllegalStateException("An object of type " + className + " cannot be instantiated", e);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("The default ctor for type " + className + " cannot be accessed", e);
        } catch (RuntimeException e) {
            throw new IllegalStateException("Exception when instantiating: " + className, e);
        } catch (InvocationTargetException e) {
            throw new IllegalStateException("The ctor with String.class arg for type " + className + " cannot be invoked", e);
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("The ctor with String.class arg for type " + className + " doesn't exist", e);
        }
    }

    private void addHeaders(HttpMethod m, RestRequest request) {
        for (RestData.Header h : request.getHeaders()) {
            m.addRequestHeader(h.getName(), h.getValue());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy