All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ingenico.direct.defaultimpl.DefaultCommunicator Maven / Gradle / Ivy

package com.ingenico.direct.defaultimpl;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpStatus;
import org.apache.http.client.utils.URIBuilder;

import com.ingenico.direct.Authenticator;
import com.ingenico.direct.CallContext;
import com.ingenico.direct.CommunicationException;
import com.ingenico.direct.Communicator;
import com.ingenico.direct.Connection;
import com.ingenico.direct.Marshaller;
import com.ingenico.direct.MetaDataProvider;
import com.ingenico.direct.NotFoundException;
import com.ingenico.direct.ParamRequest;
import com.ingenico.direct.PooledConnection;
import com.ingenico.direct.RequestHeader;
import com.ingenico.direct.RequestParam;
import com.ingenico.direct.ResponseException;
import com.ingenico.direct.ResponseHandler;
import com.ingenico.direct.ResponseHeader;
import com.ingenico.direct.logging.CommunicatorLogger;

/**
 * Used to communicate with the Ingenico ePayments platform web services.
 * 

* It contains all the logic to transform a request object to a HTTP request and * a HTTP response to a response object. *

* Thread-safe. */ public class DefaultCommunicator implements Communicator { private static final Charset CHARSET = Charset.forName("UTF-8"); private final URI apiEndpoint; private final Connection connection; private final Authenticator authenticator; private final MetaDataProvider metaDataProvider; private final Marshaller marshaller; public DefaultCommunicator(URI apiEndpoint, Connection connection, Authenticator authenticator, MetaDataProvider metaDataProvider, Marshaller marshaller) { if (apiEndpoint == null) { throw new IllegalArgumentException("apiEndpoint is required"); } if (apiEndpoint.getPath() != null && !apiEndpoint.getPath().isEmpty()) { throw new IllegalArgumentException("apiEndpoint should not contain a path"); } if (apiEndpoint.getUserInfo() != null || apiEndpoint.getQuery() != null || apiEndpoint.getFragment() != null) { throw new IllegalArgumentException("apiEndpoint should not contain user info, query or fragment"); } if (connection == null) { throw new IllegalArgumentException("connection is required"); } if (authenticator == null) { throw new IllegalArgumentException("authenticator is required"); } if (metaDataProvider == null) { throw new IllegalArgumentException("metaDataProvider is required"); } if (marshaller == null) { throw new IllegalArgumentException("marshaller is required"); } this.apiEndpoint = apiEndpoint; this.connection = connection; this.authenticator = authenticator; this.metaDataProvider = metaDataProvider; this.marshaller = marshaller; } /** * Releases any system resources associated with this object. */ @Override public void close() throws IOException { connection.close(); } /** {@inheritDoc} */ @Override @SuppressWarnings("resource") public O get(final String relativePath, List requestHeaders, ParamRequest requestParameters, final Class responseType, final CallContext context) { List requestParameterList = requestParameters == null ? null : requestParameters.toRequestParameters(); URI uri = toAbsoluteURI(relativePath, requestParameterList); if (requestHeaders == null) { requestHeaders = new ArrayList(); } addGenericHeaders("GET", uri, requestHeaders, context); return connection.get(uri, requestHeaders, getDefaultResponseHandler(relativePath, responseType, context)); } /** {@inheritDoc} */ @Override @SuppressWarnings("resource") public O delete(String relativePath, List requestHeaders, ParamRequest requestParameters, final Class responseType, final CallContext context) { List requestParameterList = requestParameters == null ? null : requestParameters.toRequestParameters(); URI uri = toAbsoluteURI(relativePath, requestParameterList); if (requestHeaders == null) { requestHeaders = new ArrayList(); } addGenericHeaders("DELETE", uri, requestHeaders, context); return connection.delete(uri, requestHeaders, getDefaultResponseHandler(relativePath, responseType, context)); } /** {@inheritDoc} */ @Override @SuppressWarnings("resource") public O post(final String relativePath, List requestHeaders, ParamRequest requestParameters, Object requestBody, final Class responseType, final CallContext context) { List requestParameterList = requestParameters == null ? null : requestParameters.toRequestParameters(); URI uri = toAbsoluteURI(relativePath, requestParameterList); if (requestHeaders == null) { requestHeaders = new ArrayList(); } String requestJson = prepareBody(requestHeaders, requestBody); addGenericHeaders("POST", uri, requestHeaders, context); return connection.post(uri, requestHeaders, requestJson, getDefaultResponseHandler(relativePath, responseType, context)); } /** {@inheritDoc} */ @Override @SuppressWarnings("resource") public O put(final String relativePath, List requestHeaders, ParamRequest requestParameters, Object requestBody, final Class responseType, final CallContext context) { List requestParameterList = requestParameters == null ? null : requestParameters.toRequestParameters(); URI uri = toAbsoluteURI(relativePath, requestParameterList); if (requestHeaders == null) { requestHeaders = new ArrayList(); } String requestJson = prepareBody(requestHeaders, requestBody); addGenericHeaders("PUT", uri, requestHeaders, context); return connection.put(uri, requestHeaders, requestJson, getDefaultResponseHandler(relativePath, responseType, context)); } public URI getApiEndpoint() { return apiEndpoint; } @Override public Connection getConnection() { return connection; } @Override public Authenticator getAuthenticator() { return authenticator; } @Override public MetaDataProvider getMetaDataProvider() { return metaDataProvider; } @Override public Marshaller getMarshaller() { return marshaller; } protected URI toAbsoluteURI(String relativePath, List requestParameters) { if (apiEndpoint.getPath() != null && !apiEndpoint.getPath().isEmpty()) { throw new IllegalStateException("apiEndpoint should not contain a path"); } if (apiEndpoint.getUserInfo() != null || apiEndpoint.getQuery() != null || apiEndpoint.getFragment() != null) { throw new IllegalStateException("apiEndpoint should not contain user info, query or fragment"); } String absolutePath; if (relativePath.startsWith("/")) { absolutePath = relativePath; } else { absolutePath = "/" + relativePath; } URIBuilder uriBuilder = new URIBuilder() .setScheme(apiEndpoint.getScheme()) .setHost(apiEndpoint.getHost()) .setPort(apiEndpoint.getPort()) .setPath(absolutePath); if (requestParameters != null) { for (RequestParam nvp: requestParameters) { uriBuilder.addParameter(nvp.getName(), nvp.getValue()); } } try { return uriBuilder.build(); } catch (URISyntaxException e) { throw new IllegalArgumentException("Unable to construct URI", e); } } private String prepareBody(List requestHeaders, Object requestBody) { String requestJson = null; if (requestBody != null) { requestHeaders.add(new RequestHeader("Content-Type", "application/json")); requestJson = marshaller.marshal(requestBody); } return requestJson; } /** * Adds the necessary headers to the given list of headers. This includes the authorization header, which uses * other headers, so when you need to override this method, make sure to call {@code super.addGenericHeaders} * at the end of your overridden method. */ protected void addGenericHeaders(String httpMethod, URI uri, List requestHeaders, CallContext context) { // add server meta info headers requestHeaders.addAll(metaDataProvider.getServerMetaDataHeaders()); // add date header requestHeaders.add(new RequestHeader("Date", getHeaderDateString())); // add context specific headers if (context != null && context.getIdempotenceKey() != null) { requestHeaders.add(new RequestHeader("X-GCS-Idempotence-Key", context.getIdempotenceKey())); } // add signature String authenticationSignature = authenticator.createSimpleAuthenticationSignature(httpMethod, uri, requestHeaders); requestHeaders.add(new RequestHeader("Authorization", authenticationSignature)); } /** * Returns the date in the preferred format for the HTTP date header (RFC1123). */ protected String getHeaderDateString() { Calendar calendar = Calendar.getInstance(); SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); return dateFormat.format(calendar.getTime()); } private ResponseHandler getDefaultResponseHandler(final String relativePath, final Class responseType, final CallContext context) { return new ResponseHandler() { @Override public O handleResponse(int statusCode, InputStream bodyStream, List headers) { return processResponse(statusCode, bodyStream, headers, responseType, relativePath, context); } }; } protected O processResponse(int statusCode, InputStream bodyStream, List headers, Class responseType, String requestPath, CallContext context) { if (context != null) { updateContext(headers, context); } throwExceptionIfNecessary(statusCode, bodyStream, headers, requestPath); return marshaller.unmarshal(bodyStream, responseType); } /** * Updates the given call context based on the contents of the given response. */ protected void updateContext(List headers, CallContext context) { String idempotenceRequestTimestampValue = ResponseHeader.getHeaderValue(headers, "X-GCS-Idempotence-Request-Timestamp"); if (idempotenceRequestTimestampValue != null) { Long idempotenceRequestTimestamp = Long.valueOf(idempotenceRequestTimestampValue); context.setIdempotenceRequestTimestamp(idempotenceRequestTimestamp); } else { context.setIdempotenceRequestTimestamp(null); } } /** * Checks the status code and headers for errors and throws an exception if necessary. */ protected void throwExceptionIfNecessary(int statusCode, InputStream bodyStream, List headers, String requestPath) { // status codes in the 100 or 300 range are not expected if (statusCode < 200 || statusCode >= 300) { String body = toString(bodyStream); if (!body.isEmpty() && !isJson(headers)) { ResponseException cause = new ResponseException(statusCode, body, headers); if (statusCode == HttpStatus.SC_NOT_FOUND) { throw new NotFoundException("The requested resource was not found; invalid path: " + requestPath, cause); } else { throw new CommunicationException(cause); } } else { throw new ResponseException(statusCode, body, headers); } } } private String toString(InputStream bodyStream) { try { Reader reader = new InputStreamReader(bodyStream, CHARSET); StringBuilder body = new StringBuilder(); char[] buffer = new char[4096]; int len; while ((len = reader.read(buffer)) != -1) { body.append(buffer, 0, len); } return body.toString(); } catch (IOException e) { throw new CommunicationException(e); } } private boolean isJson(List headers) { String contentType = ResponseHeader.getHeaderValue(headers, "Content-Type"); return contentType == null || "application/json".equalsIgnoreCase(contentType) || contentType.toLowerCase().startsWith("application/json"); } /** * Utility method that delegates the call to this communicator's session's connection if that's an instance of * {@link PooledConnection}. If not this method does nothing. * @see PooledConnection#closeIdleConnections(long, TimeUnit) */ @Override public void closeIdleConnections(long idleTime, TimeUnit timeUnit) { if (connection instanceof PooledConnection) { ((PooledConnection) connection).closeIdleConnections(idleTime, timeUnit); } } /** * Utility method that delegates the call to this communicator's session's connection if that's an instance of * {@link PooledConnection}. If not this method does nothing. * @see PooledConnection#closeExpiredConnections() */ @Override public void closeExpiredConnections() { if (connection instanceof PooledConnection) { ((PooledConnection) connection).closeExpiredConnections(); } } @Override public void enableLogging(CommunicatorLogger communicatorLogger) { // delegate to the connection connection.enableLogging(communicatorLogger); } @Override public void disableLogging() { // delegate to the connection connection.disableLogging(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy