| /******************************************************************************* |
| * 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(); |
| } |
| |
| /** |
| * 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 (responseCode == HttpStatus.SC_OK) { |
| // Get response bytes |
| byte[] responseBytes = httpMethod.getResponseBody(); |
| String responseCharSet = null; |
| if (httpMethod instanceof HttpMethodBase) { |
| HttpMethodBase methodBase = (HttpMethodBase) httpMethod; |
| responseCharSet = methodBase.getRequestCharSet(); |
| } |
| // Get responseBody as String |
| responseBody = getResponseAsString(responseBytes, responseCharSet); |
| } else |
| handleException("Http response not OK. URL=" + uri + " responseCode=" + new Integer(responseCode), null, responseCode); //$NON-NLS-1$ //$NON-NLS-2$ |
| } 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), e, responseCode); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return result; |
| } |
| |
| 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) throws RestException { |
| logException(message, e); |
| throw new RestException(message, e, responseCode); |
| } |
| |
| 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=" + callable; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| 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)); |
| } |
| |
| } |