| /******************************************************************************* |
| * 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; |
| } |
| } |