org.eclipse.ecf.remoteservice.rest.client.RestClientService.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ecf.remoteservice.rest.client.RestClientService.java

Source

/******************************************************************************* 
 * Copyright (c) 2009 EclipseSource and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   EclipseSource - initial API and implementation
 *******************************************************************************/
package org.eclipse.ecf.remoteservice.rest.client;

import java.io.IOException;
import java.io.NotSerializableException;
import java.util.*;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.util.EncodingUtil;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ecf.core.security.*;
import org.eclipse.ecf.core.util.ECFException;
import org.eclipse.ecf.internal.remoteservice.rest.Activator;
import org.eclipse.ecf.remoteservice.IRemoteCall;
import org.eclipse.ecf.remoteservice.IRemoteService;
import org.eclipse.ecf.remoteservice.client.*;
import org.eclipse.ecf.remoteservice.rest.IRestCall;
import org.eclipse.ecf.remoteservice.rest.RestException;

/**
 * This class represents a REST service from the client side of view. So a
 * RESTful web service can be accessed via the methods provided by this class.
 * Mostly the methods are inherited from {@link IRemoteService}.
 */
public class RestClientService extends AbstractClientService {

    protected final static int DEFAULT_RESPONSE_BUFFER_SIZE = 1024;

    protected final static String DEFAULT_HTTP_CONTENT_CHARSET = "UTF-8"; //$NON-NLS-1$

    protected HttpClient httpClient;
    protected int responseBufferSize = DEFAULT_RESPONSE_BUFFER_SIZE;

    public RestClientService(RestClientContainer container, RemoteServiceClientRegistration registration) {
        super(container, registration);
        this.httpClient = new HttpClient();
    }

    private boolean isResponseOk(int httpResponseCode) {
        int isOkCode = httpResponseCode - 200;
        return (isOkCode >= 0 && isOkCode < 100);
    }

    /**
     * Calls the Rest service with given URL of IRestCall. The returned value is
     * the response body as an InputStream.
     * 
     * @param call
     *            The remote call to make.  Must not be <code>null</code>.
     * @param callable
     *            The callable with default parameters to use to make the call.
     * @return The InputStream of the response body or <code>null</code> if an
     *         error occurs.
     */
    protected Object invokeRemoteCall(final IRemoteCall call, final IRemoteCallable callable) throws ECFException {
        String uri = prepareEndpointAddress(call, callable);
        HttpMethod httpMethod = createAndPrepareHttpMethod(uri, call, callable);
        // execute method
        String responseBody = null;
        int responseCode = -1;
        try {
            responseCode = httpClient.executeMethod(httpMethod);
            if (isResponseOk(responseCode)) {
                // Get responseBody as String
                responseBody = getResponseAsString(httpMethod);
            } else {
                // If this method returns true, we should retrieve the response body
                if (retrieveErrorResponseBody(responseCode)) {
                    responseBody = getResponseAsString(httpMethod);
                }
                // Now pass to the exception handler
                handleException("Http response not OK.  URL=" + uri + " responseCode=" + new Integer(responseCode), //$NON-NLS-1$//$NON-NLS-2$
                        null, responseCode, responseBody);
            }
        } catch (HttpException e) {
            handleException("Transport HttpException", e, responseCode); //$NON-NLS-1$
        } catch (IOException e) {
            handleException("Transport IOException", e, responseCode); //$NON-NLS-1$
        }
        Object result = null;
        try {
            result = processResponse(uri, call, callable, convertResponseHeaders(httpMethod.getResponseHeaders()),
                    responseBody);
        } catch (NotSerializableException e) {
            handleException(
                    "Exception deserializing response.  URL=" + uri + " responseCode=" + new Integer(responseCode), //$NON-NLS-1$//$NON-NLS-2$
                    e, responseCode);
        }
        return result;
    }

    protected boolean retrieveErrorResponseBody(int responseCode) {
        // XXX this needs to be defined differently for 
        return false;
    }

    protected String getResponseAsString(HttpMethod httpMethod) throws IOException {
        // Get response bytes
        byte[] responseBytes = httpMethod.getResponseBody();
        String responseCharSet = null;
        if (httpMethod instanceof HttpMethodBase) {
            HttpMethodBase methodBase = (HttpMethodBase) httpMethod;
            responseCharSet = methodBase.getRequestCharSet();
        }
        return getResponseAsString(responseBytes, responseCharSet);
    }

    protected String getResponseAsString(byte[] bytes, String responseCharSet) {
        if (bytes == null)
            return null;
        return EncodingUtil.getString(bytes, responseCharSet);
    }

    protected void handleException(String message, Throwable e, int responseCode, String responseBody)
            throws RestException {
        logException(message, e);
        throw new RestException(message, e, responseCode, responseBody);
    }

    protected void handleException(String message, Throwable e, int responseCode) throws RestException {
        handleException(message, e, responseCode, null);
    }

    protected void setupTimeouts(HttpClient httpClient, IRemoteCall call, IRemoteCallable callable) {
        long callTimeout = call.getTimeout();
        if (callTimeout == IRemoteCall.DEFAULT_TIMEOUT)
            callTimeout = callable.getDefaultTimeout();

        int timeout = (int) callTimeout;
        httpClient.getHttpConnectionManager().getParams().setSoTimeout(timeout);
        httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(timeout);
        httpClient.getParams().setConnectionManagerTimeout(timeout);
    }

    private Map convertResponseHeaders(Header[] headers) {
        Map result = new HashMap();
        if (headers == null)
            return result;
        for (int i = 0; i < headers.length; i++) {
            String name = headers[i].getName();
            String value = headers[i].getValue();
            result.put(name, value);
        }
        return result;
    }

    protected void addRequestHeaders(HttpMethod httpMethod, IRemoteCall call, IRemoteCallable callable) {
        // Add request headers from the callable
        Map requestHeaders = (callable.getRequestType() instanceof AbstractRequestType)
                ? ((AbstractRequestType) callable.getRequestType()).getDefaultRequestHeaders()
                : new HashMap();
        if (requestHeaders == null)
            requestHeaders = new HashMap();

        if (call instanceof IRestCall) {
            Map callHeaders = ((IRestCall) call).getRequestHeaders();
            if (callHeaders != null)
                requestHeaders.putAll(requestHeaders);
        }

        Set keySet = requestHeaders.keySet();
        Object[] headers = keySet.toArray();
        for (int i = 0; i < headers.length; i++) {
            String key = (String) headers[i];
            String value = (String) requestHeaders.get(key);
            httpMethod.addRequestHeader(key, value);
        }
    }

    protected HttpMethod createAndPrepareHttpMethod(String uri, IRemoteCall call, IRemoteCallable callable)
            throws RestException {
        HttpMethod httpMethod = null;

        IRemoteCallableRequestType requestType = callable.getRequestType();
        if (requestType == null)
            throw new RestException("Request type for call cannot be null"); //$NON-NLS-1$
        try {
            if (requestType instanceof HttpGetRequestType) {
                httpMethod = prepareGetMethod(uri, call, callable);
            } else if (requestType instanceof HttpPostRequestType) {
                httpMethod = preparePostMethod(uri, call, callable);
            } else if (requestType instanceof HttpPutRequestType) {
                httpMethod = preparePutMethod(uri, call, callable);
            } else if (requestType instanceof HttpDeleteRequestType) {
                httpMethod = prepareDeleteMethod(uri, call, callable);
            } else {
                throw new RestException("HTTP method " + requestType + " not supported"); //$NON-NLS-1$ //$NON-NLS-2$
            }
        } catch (NotSerializableException e) {
            String message = "Could not serialize parameters for uri=" + uri + " call=" + call + " callable=" //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                    + callable;
            logException(message, e);
            throw new RestException(message);
        }
        // add additional request headers
        addRequestHeaders(httpMethod, call, callable);
        // handle authentication
        setupAuthenticaton(httpClient, httpMethod);
        // needed because a resource can link to another resource
        httpClient.getParams().setParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, new Boolean(true));
        httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, DEFAULT_HTTP_CONTENT_CHARSET);
        setupTimeouts(httpClient, call, callable);
        return httpMethod;
    }

    /**
     * @throws RestException  
     */
    protected HttpMethod prepareDeleteMethod(String uri, IRemoteCall call, IRemoteCallable callable)
            throws RestException {
        return new DeleteMethod(uri);
    }

    protected HttpMethod preparePutMethod(String uri, IRemoteCall call, IRemoteCallable callable)
            throws NotSerializableException {
        PutMethod result = new PutMethod(uri);
        HttpPutRequestType putRequestType = (HttpPutRequestType) callable.getRequestType();

        IRemoteCallParameter[] defaultParameters = callable.getDefaultParameters();
        Object[] parameters = call.getParameters();

        if (putRequestType.useRequestEntity()) {
            if (defaultParameters != null && defaultParameters.length > 0 && parameters != null
                    && parameters.length > 0) {
                RequestEntity requestEntity = putRequestType.generateRequestEntity(uri, call, callable,
                        defaultParameters[0], parameters[0]);
                result.setRequestEntity(requestEntity);
            }
        } else {
            NameValuePair[] params = toNameValuePairs(uri, call, callable);
            if (params != null)
                result.setQueryString(params);
        }
        return result;
    }

    /**
     * @throws ECFException  
     */
    protected HttpMethod preparePostMethod(String uri, IRemoteCall call, IRemoteCallable callable)
            throws NotSerializableException {
        PostMethod result = new PostMethod(uri);
        HttpPostRequestType postRequestType = (HttpPostRequestType) callable.getRequestType();

        IRemoteCallParameter[] defaultParameters = callable.getDefaultParameters();
        Object[] parameters = call.getParameters();
        if (postRequestType.useRequestEntity()) {
            if (defaultParameters != null && defaultParameters.length > 0 && parameters != null
                    && parameters.length > 0) {
                RequestEntity requestEntity = postRequestType.generateRequestEntity(uri, call, callable,
                        defaultParameters[0], parameters[0]);
                result.setRequestEntity(requestEntity);
            }
        } else {
            NameValuePair[] params = toNameValuePairs(uri, call, callable);
            if (params != null)
                result.setQueryString(params);
        }
        return result;
    }

    /**
     * @throws ECFException  
     */
    protected HttpMethod prepareGetMethod(String uri, IRemoteCall call, IRemoteCallable callable)
            throws NotSerializableException {
        HttpMethod result = new GetMethod(uri);
        NameValuePair[] params = toNameValuePairs(uri, call, callable);
        if (params != null)
            result.setQueryString(params);
        return result;
    }

    protected NameValuePair[] toNameValuePairs(String uri, IRemoteCall call, IRemoteCallable callable)
            throws NotSerializableException {
        IRemoteCallParameter[] restParameters = prepareParameters(uri, call, callable);
        List nameValueList = new ArrayList();
        if (restParameters != null) {
            for (int i = 0; i < restParameters.length; i++) {
                String parameterValue = null;
                Object o = restParameters[i].getValue();
                if (o instanceof String) {
                    parameterValue = (String) o;
                } else if (o != null) {
                    parameterValue = o.toString();
                }
                if (parameterValue != null) {
                    nameValueList.add(new NameValuePair(restParameters[i].getName(), parameterValue));
                }
            }
        }
        return (NameValuePair[]) nameValueList.toArray(new NameValuePair[nameValueList.size()]);
    }

    protected void setupAuthenticaton(HttpClient httpClient, HttpMethod method) {
        IConnectContext connectContext = container.getConnectContextForAuthentication();
        if (connectContext != null) {
            NameCallback nameCallback = new NameCallback(""); //$NON-NLS-1$
            ObjectCallback passwordCallback = new ObjectCallback();
            Callback[] callbacks = new Callback[] { nameCallback, passwordCallback };
            CallbackHandler callbackHandler = connectContext.getCallbackHandler();
            if (callbackHandler == null)
                return;
            try {
                callbackHandler.handle(callbacks);
                String username = nameCallback.getName();
                String password = (String) passwordCallback.getObject();
                AuthScope authscope = new AuthScope(null, -1);
                Credentials credentials = new UsernamePasswordCredentials(username, password);
                httpClient.getState().setCredentials(authscope, credentials);
                method.setDoAuthentication(true);
            } catch (IOException e) {
                logException("IOException setting credentials for rest httpclient", e); //$NON-NLS-1$
            } catch (UnsupportedCallbackException e) {
                logException("UnsupportedCallbackException setting credentials for rest httpclient", e); //$NON-NLS-1$
            }

        }
    }

    protected void logException(String string, Throwable e) {
        Activator a = Activator.getDefault();
        if (a != null)
            a.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, string, e));
    }

    protected void logWarning(String string, Throwable e) {
        Activator a = Activator.getDefault();
        if (a != null)
            a.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, string));
    }

}