blob: ad6440004370d96c9b3f03b559edfddfdc7a2759 [file] [log] [blame]
/*******************************************************************************
* 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));
}
}