com.helger.peppol.httpclient.AbstractGenericSMPClient.java Source code

Java tutorial

Introduction

Here is the source code for com.helger.peppol.httpclient.AbstractGenericSMPClient.java

Source

/**
 * Copyright (C) 2015-2016 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * Version: MPL 1.1/EUPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at:
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Copyright The PEPPOL project (http://www.peppol.eu)
 *
 * Alternatively, the contents of this file may be used under the
 * terms of the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL
 * (the "Licence"); You may not use this work except in compliance
 * with the Licence.
 * You may obtain a copy of the Licence at:
 * http://joinup.ec.europa.eu/software/page/eupl/licence-eupl
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 *
 * If you wish to allow use of your version of this file only
 * under the terms of the EUPL License and not to allow others to use
 * your version of this file under the MPL, indicate your decision by
 * deleting the provisions above and replace them with the notice and
 * other provisions required by the EUPL License. If you do not delete
 * the provisions above, a recipient may use your version of this file
 * under either the MPL or the EUPL License.
 */
package com.helger.peppol.httpclient;

import java.io.IOException;
import java.net.URI;
import java.net.UnknownHostException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.OverrideOnDemand;
import com.helger.commons.lang.GenericReflection;
import com.helger.commons.string.ToStringGenerator;
import com.helger.peppol.smpclient.SMPClientConfiguration;
import com.helger.peppol.smpclient.exception.SMPClientBadRequestException;
import com.helger.peppol.smpclient.exception.SMPClientException;
import com.helger.peppol.smpclient.exception.SMPClientNotFoundException;
import com.helger.peppol.smpclient.exception.SMPClientUnauthorizedException;

public abstract class AbstractGenericSMPClient<IMPLTYPE extends AbstractGenericSMPClient<IMPLTYPE>> {
    private static final Logger s_aLogger = LoggerFactory.getLogger(AbstractGenericSMPClient.class);

    /**
     * The string representation of the SMP host URL, always ending with a
     * trailing slash!
     */
    private final String m_sSMPHost;

    private HttpHost m_aProxy;
    private int m_nConnectionTimeoutMS = 5000;
    private int m_nRequestTimeoutMS = 10000;

    /**
     * Constructor with a direct SMP URL.<br>
     * Remember: must be HTTP and using port 80 only!
     *
     * @param aSMPHost
     *        The address of the SMP service. Must be port 80 and basic http only
     *        (no https!). Example: http://smpcompany.company.org
     */
    public AbstractGenericSMPClient(@Nonnull final URI aSMPHost) {
        ValueEnforcer.notNull(aSMPHost, "SMPHost");

        if (!"http".equals(aSMPHost.getScheme()))
            s_aLogger.warn("SMP URI " + aSMPHost + " does not use the expected http scheme!");
        // getPort () returns -1 if none was explicitly specified
        if (aSMPHost.getPort() != 80 && aSMPHost.getPort() != -1)
            s_aLogger.warn("SMP URI " + aSMPHost + " is not running on port 80!");

        // Build string and ensure it ends with a "/"
        final String sSMPHost = aSMPHost.toString();
        m_sSMPHost = sSMPHost.endsWith("/") ? sSMPHost : sSMPHost + '/';

        // Set default proxy from configuration file
        m_aProxy = SMPClientConfiguration.getHttpProxy();
    }

    @Nonnull
    protected final IMPLTYPE thisAsT() {
        return GenericReflection.uncheckedCast(this);
    }

    /**
     * @return The SMP host URI string we're operating on. Never <code>null</code>
     *         . Always has a trailing "/".
     */
    @Nonnull
    public String getSMPHostURI() {
        return m_sSMPHost;
    }

    /**
     * @return The HTTP proxy to be used to access the SMP server. Is
     *         <code>null</code> by default.
     */
    @Nullable
    public HttpHost getProxy() {
        return m_aProxy;
    }

    /**
     * Set the proxy to be used to access the SMP server. Note: proxy
     * authentication is currently not supported!
     *
     * @param aProxy
     *        May be <code>null</code> to indicate no proxy.
     * @return this for chaining
     */
    @Nonnull
    public IMPLTYPE setProxy(@Nullable final HttpHost aProxy) {
        m_aProxy = aProxy;
        return thisAsT();
    }

    /**
     * @return The connection timeout in milliseconds. Defaults to 5000 (5 secs).
     */
    public int getConnectionTimeoutMS() {
        return m_nConnectionTimeoutMS;
    }

    /**
     * Set the connection timeout in milliseconds.
     *
     * @param nConnectionTimeoutMS
     *        The connection timeout milliseconds to use. Only values &gt; 0 are
     *        considered.
     * @return this for chaining
     */
    @Nonnull
    public IMPLTYPE setConnectionTimeoutMS(final int nConnectionTimeoutMS) {
        m_nConnectionTimeoutMS = nConnectionTimeoutMS;
        return thisAsT();
    }

    /**
     * @return The request timeout in milliseconds. Defaults to 10000 (10 secs).
     */
    public int getRequestTimeoutMS() {
        return m_nRequestTimeoutMS;
    }

    /**
     * Set the request timeout in milliseconds.
     *
     * @param nRequestTimeoutMS
     *        The request timeout milliseconds to use. Only values &gt; 0 are
     *        considered.
     * @return this for chaining
     */
    @Nonnull
    public IMPLTYPE setRequestTimeoutMS(final int nRequestTimeoutMS) {
        m_nRequestTimeoutMS = nRequestTimeoutMS;
        return thisAsT();
    }

    /**
     * The main execution routine. Overwrite this method to add additional
     * properties to the call.
     *
     * @param aRequest
     *        The request to be executed. Never <code>null</code>.
     * @return The HTTP execution response. Never <code>null</code>.
     * @throws IOException
     *         On HTTP error
     * @see #setProxy(HttpHost)
     * @see #setConnectionTimeoutMS(int)
     * @see #setRequestTimeoutMS(int)
     */
    @Nonnull
    @OverrideOnDemand
    protected Response executeRequest(@Nonnull final Request aRequest) throws IOException {
        if (m_aProxy != null)
            aRequest.viaProxy(m_aProxy);
        if (m_nConnectionTimeoutMS > 0)
            aRequest.connectTimeout(m_nConnectionTimeoutMS);
        if (m_nRequestTimeoutMS > 0)
            aRequest.socketTimeout(m_nRequestTimeoutMS);

        return aRequest.execute();
    }

    /**
     * Execute a generic request on the SMP. This is e.g. helpful for accessing
     * the PEPPOL Directory BusinessCard API. Compared to
     * {@link #executeGenericRequest(Request, ResponseHandler)} this method does
     * NOT convert the {@link IOException} from HTTP communication problems to
     * {@link IOException}.
     *
     * @param aRequest
     *        The request to be executed. The proxy + connection and request
     *        timeout are set in this method.
     * @param aResponseHandler
     *        The response handler to be used. May not be <code>null</code>.
     * @return The return value of the response handler.
     * @throws IOException
     *         On HTTP communication error
     * @see #executeGenericRequest(Request, ResponseHandler)
     */
    @Nonnull
    public <T> T executeRequest(@Nonnull final Request aRequest, @Nonnull final ResponseHandler<T> aResponseHandler)
            throws IOException {
        return executeRequest(aRequest).handleResponse(aResponseHandler);
    }

    /**
     * Convert the passed generic HTTP exception into a more specific exception.
     *
     * @param ex
     *        The generic exception. May not be <code>null</code>.
     * @return A new SMP specific exception, using the passed exception as the
     *         cause.
     */
    @Nonnull
    public static SMPClientException getConvertedException(@Nonnull final Exception ex) {
        if (ex instanceof SMPClientException)
            return (SMPClientException) ex;

        if (ex instanceof HttpResponseException) {
            final HttpResponseException hex = (HttpResponseException) ex;
            final int nHttpStatus = hex.getStatusCode();
            switch (nHttpStatus) {
            case HttpStatus.SC_BAD_REQUEST:
                return new SMPClientBadRequestException(hex);
            case HttpStatus.SC_FORBIDDEN:
                return new SMPClientUnauthorizedException(hex);
            case HttpStatus.SC_NOT_FOUND:
                return new SMPClientNotFoundException(hex);
            }
            return new SMPClientException("Error thrown with HTTP status code " + nHttpStatus, hex);
        }

        // Special case
        if (ex instanceof UnknownHostException)
            return new SMPClientNotFoundException((UnknownHostException) ex);

        // Generic version
        return new SMPClientException("Unknown error thrown by SMP server (" + ex.getMessage() + ")", ex);
    }

    /**
     * Execute a generic request on the SMP. This is e.g. helpful for accessing
     * the PEPPOL Directory BusinessCard API. This is equivalent to
     * {@link #executeRequest(Request, ResponseHandler)} but includes the
     * conversion of Exceptions to {@link SMPClientException} objects.
     *
     * @param aRequest
     *        The request to be executed. The proxy + connection and request
     *        timeout are set in this method.
     * @param aResponseHandler
     *        The response handler to be used. May not be <code>null</code>.
     * @return The return value of the response handler.
     * @throws SMPClientException
     *         One of the converted exceptions
     * @see #executeRequest(Request, ResponseHandler)
     * @see #getConvertedException(Exception)
     */
    @Nonnull
    public <T> T executeGenericRequest(@Nonnull final Request aRequest,
            @Nonnull final ResponseHandler<T> aResponseHandler) throws SMPClientException {
        try {
            return executeRequest(aRequest, aResponseHandler);
        } catch (final Exception ex) {
            throw getConvertedException(ex);
        }
    }

    @Override
    public String toString() {
        return new ToStringGenerator(this).append("SMPHost", m_sSMPHost).appendIfNotNull("Proxy", m_aProxy)
                .append("ConnectionTimeoutMS", m_nConnectionTimeoutMS)
                .append("RequestTimeoutMS", m_nRequestTimeoutMS).toString();
    }
}