blob: e3d4498e54a504bf64559cab8a6663e4f8067f7d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012-2014 SAP SE.
* 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:
* SAP SE - initial API and implementation and/or initial documentation
*
*******************************************************************************/
package org.eclipse.ogee.client.connectivity.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocketFactory;
import org.eclipse.ogee.client.activator.Activator;
import org.eclipse.ogee.client.connectivity.api.IRestClient;
import org.eclipse.ogee.client.connectivity.api.IRestRequest;
import org.eclipse.ogee.client.connectivity.api.IRestResponse;
import org.eclipse.ogee.client.connectivity.api.IServerConnectionParameters;
import org.eclipse.ogee.client.connectivity.interceptors.BasicAuthenticationResponseInterceptor;
import org.eclipse.ogee.client.connectivity.security.IBasicAuthentication;
import org.eclipse.ogee.client.connectivity.security.X509Authentication;
import org.eclipse.ogee.client.exceptions.RestClientException;
import org.eclipse.ogee.client.nls.messages.Messages;
import org.eclipse.ogee.client.parser.Representation;
import org.eclipse.ogee.client.util.Base64;
import org.eclipse.ogee.client.util.TraceLogger;
import org.eclipse.ogee.client.util.Util;
/**
* Default implementation of IRestClient.
*/
public class DefaultRestClient extends RestClientBase implements IRestClient {
private static final TraceLogger traceLogger = TraceLogger.getLogger(Activator.PLUGIN_ID);
protected static final String CONTENT_TYPE = "Content-Type"; //$NON-NLS-1$
protected static final String MULTIPART_MIXED = "multipart/mixed"; //$NON-NLS-1$
protected static final String SOCKET_CLOSED = "socket closed"; //$NON-NLS-1$
protected final static Logger LOGGER = Logger
.getLogger(DefaultRestClient.class.getName());
protected IServerConnectionParameters serverConnection;
protected X509Authentication x509Authentication;
protected Map<String, String> cookies = new HashMap<String, String>();
protected SSLSocketFactory sslSocketFactory;
protected Representation representation;
private HttpURLConnection httpURLConnectionRef;
public static final String UTF_8 = "UTF-8"; //$NON-NLS-1$
/**
* Constructs a new Default Rest Client with the given server connection
* details and user credentials.
*
* @param serverConnectionDetails
* - the details of the server.
* @param basicAuthentication
* - the user name and password.
*/
public DefaultRestClient(
IServerConnectionParameters serverConnectionDetails,
IBasicAuthentication basicAuthentication) {
constructBasicClient(serverConnectionDetails, basicAuthentication);
}
/**
* Constructs a new Default Rest Client with the given server connection
* details and X509 authentication.
*
* @param serverConnectionDetails
* - the details of the server.
* @param x509Authentication
*/
public DefaultRestClient(
IServerConnectionParameters serverConnectionDetails,
X509Authentication x509Authentication) {
try {
constructX509Client(serverConnectionDetails, x509Authentication);
} catch (SSLException e) {
LOGGER.severe(e.getMessage());
}
}
/**
* Constructs a new Default Rest Client with the given server connection
* details, user credentials and representation of the resources it exposes.
*
* @param serverConnectionDetails
* - Server connection details
* @param basicAuthentication
* - User name and password
* @param representation
* - the representation of the data
*/
public DefaultRestClient(
IServerConnectionParameters serverConnectionDetails,
IBasicAuthentication basicAuthentication,
Representation representation) {
constructBasicClient(serverConnectionDetails, basicAuthentication);
this.representation = representation;
}
/**
* Constructs a new Default Rest Client with the given server connection
* details, X509 authentication and representation of the resources it
* exposes.
*
* @param serverConnectionDetails
* - Server connection details
* @param x509Authentication
* @param representation
* - the representation of the data
*/
public DefaultRestClient(
IServerConnectionParameters serverConnectionDetails,
X509Authentication x509Authentication, Representation representation) {
try {
constructX509Client(serverConnectionDetails, x509Authentication);
} catch (SSLException e) {
LOGGER.severe(e.getMessage());
}
this.representation = representation;
}
protected IRestResponse handleRedirect(IRestRequest restRequest,
IRestResponse restResponse) throws RestClientException {
traceLogger.trace("Inside Handle Request Redirect method");
int statusCode = restResponse.getStatusCode();
String location = restResponse.getHeader("location");
restResponse.getBody();
traceLogger.trace("Response Status code is: %1$s. The location is: %2$s The response body is: %3$s ",statusCode,location,restResponse.getBody());
if (location != null && statusCode == 307) {
URI locationUri = URI.create(location);
if (locationUri.getScheme() == null) {
URI requestUri = URI.create(restRequest.getUrl());
String port = (String) (requestUri.getPort() == -1 ? "" : ":"
+ requestUri.getPort());
String refererHost = requestUri.getScheme() + "://"
+ requestUri.getHost() + port;
location = refererHost + location;
}
List<String> setCookies = restResponse.getHeaders("set-cookie");
if (setCookies != null) {
for (String cookie : setCookies) {
if (cookie == null)
continue;
cookie = cookie.substring(0, cookie.indexOf(";")); //$NON-NLS-1$
String cookieName = cookie
.substring(0, cookie.indexOf("=")); //$NON-NLS-1$
String cookieValue = cookie.substring(
cookie.indexOf("=") + 1, cookie.length()); //$NON-NLS-1$
this.cookies.put(cookieName, cookieValue);
}
}
traceLogger.trace("Modified URL for redirected request is: %1$s",location);
IRestRequest request = new RestRequest(location);
IRestResponse response = send(request);
return response;
}
return restResponse;
}
/**
* @param serverConnectionDetails
* @param x509Authentication
* @throws SSLException
*/
private void constructX509Client(
IServerConnectionParameters serverConnectionDetails,
X509Authentication x509Authentication) throws SSLException {
traceLogger.trace("Inside constructX509Client method");
if (Boolean.valueOf(serverConnectionDetails.get(
IServerConnectionParameters.USE_SSL).toString()) == false){
traceLogger.trace("SSLException occurred: %1$s",Messages.getString("DefaultRestClient.2")); //$NON-NLS-1$
throw new SSLException(Messages.getString("DefaultRestClient.2")); //$NON-NLS-1$
}
this.serverConnection = serverConnectionDetails;
this.x509Authentication = x509Authentication;
this.setSslSocketFactory(x509Authentication.getSslSocketFactory());
this.representation = Representation.ATOM;
}
private void constructBasicClient(
IServerConnectionParameters serverConnectionDetails,
IBasicAuthentication basicAuthentication) {
traceLogger.trace("Inside constructBasicClient method");
this.serverConnection = serverConnectionDetails;
if (basicAuthentication != null) {
this.registerResponseInterceptor(new BasicAuthenticationResponseInterceptor(
basicAuthentication));
}
this.representation = Representation.ATOM;
}
private String getBaseUrl(
IServerConnectionParameters serverConnectionDetails) {
StringBuilder builder = new StringBuilder();
Object parameter = serverConnectionDetails
.get(IServerConnectionParameters.USE_SSL);
if (parameter != null) {
if (Boolean.valueOf(parameter.toString()) == true) {
builder.append("https://"); //$NON-NLS-1$
} else {
builder.append("http://"); //$NON-NLS-1$
}
} else {
builder.append("http://"); //$NON-NLS-1$
}
builder.append(serverConnectionDetails.getHost())
.append(":").append(serverConnectionDetails.getPort()); //$NON-NLS-1$
return builder.toString();
}
/**
* Formats the given string to URL string.
*
* @param urlString
* @return - the URL string
* @throws RestClientException
*/
protected String getUrl(String urlString) throws RestClientException {
urlString = urlString.toLowerCase(Locale.ENGLISH).startsWith("http") ? urlString : getBaseUrl(this.serverConnection) + urlString; //$NON-NLS-1$
urlString = urlString.replace(" ", "%20");//$NON-NLS-1$ //$NON-NLS-2$
return urlString;
}
/**
* Add the given headers to the request.
*
* @param httpURLConnection
* @param headers
*/
protected void addHeadersToRequest(HttpURLConnection httpURLConnection,
Headers headers) {
if (headers.isEmpty())
return;
for (Entry<String, List<String>> entry : headers.entrySet()) {
for (String value : entry.getValue()) {
httpURLConnection.addRequestProperty(entry.getKey(), value);
traceLogger.trace("Request Header key is: %1$s Value is: %2$s ",entry.getKey(),value);
}
}
}
/*
* (non-Javadoc)
*
* @see
* com.sap.nw.gateway.odata.client.connectivity.v2.RestClientBase#send(com
* .sap.nw.gateway.odata.client.connectivity.v2.IRestRequest)
*/
@Override
public IRestResponse send(IRestRequest restRequest)
throws RestClientException {
traceLogger.trace("Inside Send Method ");
String urlString = getUrl(restRequest.getUrl());
traceLogger.trace("Request URL is: %1$s",urlString);
OutputStreamWriter outputStreamWriter = null;
OutputStream outputStream = null;
InputStream inputStream = null;
try {
HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(
urlString).openConnection();
String userPassword = restRequest.getHeader("credentials"); //$NON-NLS-1$
if (userPassword != null && !userPassword.isEmpty()) {
String encoding = Base64.encode(userPassword);
traceLogger.trace("setting basic authorization header");
httpURLConnection.setRequestProperty(
"Authorization", "Basic " + encoding); //$NON-NLS-1$ //$NON-NLS-2$
}
this.httpURLConnectionRef = httpURLConnection;
Object parameter = this.serverConnection
.get(IServerConnectionParameters.USE_SSL);
if (parameter == null) {
// do nothing, http
traceLogger.trace("HTTP url");
} else if (Boolean.valueOf(parameter.toString()) == true) {
traceLogger.trace("HTTPS url");
if (this.sslSocketFactory != null) {
if (httpURLConnection instanceof HttpsURLConnection) {
((HttpsURLConnection) httpURLConnection)
.setSSLSocketFactory(this.sslSocketFactory);
}
}
}
httpURLConnection.setInstanceFollowRedirects(false);
httpURLConnection.setRequestMethod(restRequest.getMethod()
.toString());
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
traceLogger.trace("Request method is: %1$s",restRequest.getMethod().toString());
if (!cookies.isEmpty()) {
StringBuilder cookies = new StringBuilder();
for (Map.Entry<String, String> entry : this.cookies.entrySet()) {
cookies.append(entry.getKey())
.append("=").append(entry.getValue()).append(";"); //$NON-NLS-1$ //$NON-NLS-2$
}
cookies.deleteCharAt(cookies.lastIndexOf(";")); //$NON-NLS-1$
httpURLConnection.setRequestProperty(
ConnectivityConstants.COOKIE, cookies.toString());
traceLogger.trace(" cookies added to URL connection are: %1$s",cookies.toString());
}
// add additional headers to request
addHeadersToRequest(httpURLConnection, restRequest.getHeaders());
traceLogger.trace("Request body is: %1$s",restRequest.getBody());
if (restRequest.getBody() != null) {
httpURLConnection.setRequestProperty(
ConnectivityConstants.CONTENT_LENGTH,
Integer.toString(restRequest.getBody().length()));
}
if (restRequest.getBody() != null) {
outputStream = httpURLConnection.getOutputStream();
outputStreamWriter = new OutputStreamWriter(outputStream, UTF_8);
outputStreamWriter.write(restRequest.getBody());
outputStreamWriter.close(); // $JL-RESOURCE$
}
IRestResponse restResponse;
try {
inputStream = httpURLConnection.getInputStream();
} catch (IOException e) {
traceLogger.trace("IOException from is: %1$s",e.getMessage());
inputStream = httpURLConnection.getErrorStream();
restResponse = new RestClientException(e.getMessage());
}
Headers headers = new Headers();
for (int i = 0;; i++) {
String headerName = httpURLConnection.getHeaderFieldKey(i);
String headerValue = httpURLConnection.getHeaderField(i);
headers.put(headerName, headerValue);
traceLogger.trace("header Name is: %1$s. headervalue is: %2$s",headerName,headerValue);
if (headerName == null && headerValue == null) {
// No more headers
break;
}
}
List<String> list = headers.get(CONTENT_TYPE);
if (list == null) {
traceLogger.trace("Content type headers list is null");
restResponse = new RestResponse();
} else {
String contentTypeHeader = null;
if (headers.get(CONTENT_TYPE) != null) {
contentTypeHeader = headers.get(CONTENT_TYPE).get(0);
}
// check to see if the response should be a Batch response
if (contentTypeHeader != null
&& contentTypeHeader.contains(MULTIPART_MIXED)) {
restResponse = new BatchResponse();
} else {
restResponse = new RestResponse();
}
}
restResponse.setStatusCode(httpURLConnection.getResponseCode());
restResponse.setBody(Util.convertStreamToByteArray(inputStream));
restResponse.setHeaders(headers);
httpURLConnection.disconnect();
traceLogger.trace("Before Redirect: Response status code is: %1$s Response body is: %2$s ",restResponse.getStatusCode(),restResponse.getBody());
restResponse = handleRedirect(restRequest, restResponse);
traceLogger.trace("After Redirect: Response status code is: %1$s Response body is: %2$s ",restResponse.getStatusCode(),restResponse.getBody());
restResponse = handleRedirect(restRequest, restResponse);
return restResponse;
} catch (MalformedURLException e) {
if (e.getCause() != null) {
LOGGER.log(Level.SEVERE, e.getCause().toString(), e);
} else {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
traceLogger.trace("MalformedURL exception occurred : %1$s",e.getMessage());
throw new RestClientException(e.getMessage(), e);
} catch (ProtocolException e) {
if (e.getCause() != null) {
LOGGER.log(Level.SEVERE, e.getCause().toString(), e);
} else {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
traceLogger.trace("RestClient exception occurred: %1$s",e.getMessage());
throw new RestClientException(e.getMessage(), e);
} catch (IOException e) {
traceLogger.trace("IOException exception occurred: %1$s",e.getMessage());
if (e.getMessage().contains(SOCKET_CLOSED)) {
return new RestClientException(e.getMessage(), e);
} else {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new RestClientException(e.getMessage(), e);
}
} finally {
traceLogger.trace("End of Send method");
if (outputStreamWriter != null) {
try {
outputStreamWriter.close();
} catch (IOException e) {
if (e.getCause() != null) {
LOGGER.log(Level.SEVERE, e.getCause().toString(), e);
} else {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
}
}
}
}
/**
* Returns the SSL Socket Factory of this rest client.
*
* @return the SSL Socket Factory of this rest client.
*/
public SSLSocketFactory getSslSocketFactory() {
return sslSocketFactory;
}
/**
* Sets the SSL Socket Factory of this rest client.
*
* @param sslSocketFactory
* - the SSL Socket Factory to set.
*/
public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
}
@Override
public String getCookie(String name) {
return cookies.get(name);
}
@Override
public void setCookie(String name, String value) {
cookies.put(name, value);
}
@Override
public void setCookies(Map<String, String> cookies) {
this.cookies = cookies;
}
@Override
public Map<String, String> getCookies() {
return cookies;
}
@Override
public Representation getRepresentation() {
return representation;
}
@Override
public HttpURLConnection getHttpURLConnection() {
return this.httpURLConnectionRef;
}
}