// Path: src/main/java/uk/co/visalia/brightpearl/apiclient/account/Account.java
// public final class Account implements Serializable {
// 
//     private final Datacenter datacenter;
// 
//     private final String accountCode;
// 
//     /**
//      * Construct an account instance.
//      * @param datacenter the datacenter that hosts the account.
//      * @param accountCode the customer account code.
//      */
//     public Account(Datacenter datacenter, String accountCode) {
//         if (datacenter == null || StringUtils.isBlank(datacenter.getHost()) || StringUtils.isBlank(datacenter.getName())) {
//             throw new IllegalArgumentException("A datacenter name and host must be provided");
//         }
//         if (StringUtils.isBlank(accountCode)) {
//             throw new IllegalArgumentException("An account code must be provided");
//         }
//         this.datacenter = datacenter;
//         this.accountCode = accountCode;
//     }
// 
//     /**
//      * Returns the datacenter
//      * @return the datacenter that hosts the account.
//      */
//     public Datacenter getDatacenter() {
//         return datacenter;
//     }
// 
//     public String getAccountCode() {
//         return accountCode;
//     }
// 
//     @Override
//     public String toString() {
//         return "Account{" +
//                 "datacenter=" + datacenter +
//                 ", accountCode='" + accountCode + '\'' +
//                 '}';
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (o == null || getClass() != o.getClass()) return false;
//         Account account = (Account) o;
//         if (accountCode != null ? !accountCode.equals(account.accountCode) : account.accountCode != null) return false;
//         if (datacenter != null ? !datacenter.equals(account.datacenter) : account.datacenter != null) return false;
//         return true;
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = datacenter != null ? datacenter.hashCode() : 0;
//         result = 31 * result + (accountCode != null ? accountCode.hashCode() : 0);
//         return result;
//     }
// }
// 
// Path: src/main/java/uk/co/visalia/brightpearl/apiclient/http/Client.java
// public interface Client {
// 
//     /**
//      * Executes a request and returns the response. The status code, response body and headers should all be returned if
//      * any response is received from the server, regardless of status code. If the request fails due to a transport error,
//      * a {@link uk.co.visalia.brightpearl.apiclient.exception.BrightpearlHttpException} should be thrown.
//      * @param request The request to be executed.
//      * @return Response from the server.
//      */
//     Response execute(Request request);
// 
// }
// 
// Path: src/main/java/uk/co/visalia/brightpearl/apiclient/http/ClientFactory.java
// public interface ClientFactory {
// 
//     /**
//      * <p>
//      * Get a client to be used for a given {@link uk.co.visalia.brightpearl.apiclient.account.Account}. This method will be called
//      * for every request to be made to the Brightpearl API, so that client instances that support only a single request
//      * are supported. Implementations may return the same instance in response to multiple calls to this method, and
//      * should do so provided the client is thread-safe and supports multiple concurrent requests.
//      * </p><p>
//      * Creating a client for each customer account is not recommended but can be used as a method of throttling the
//      * resources available to each account, so the account is included as a parameter to allow this.
//      * </p>
//      * @param account The Brightpearl customer account a client is required for.
//      * @return A client instance appropriate for the account.
//      */
//     Client getClient(Account account);
// 
// }


import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import uk.co.visalia.brightpearl.apiclient.account.Account;
import uk.co.visalia.brightpearl.apiclient.http.Client;
import uk.co.visalia.brightpearl.apiclient.http.ClientFactory;
    private int connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
    private int socketTimeoutMs = DEFAULT_SOCKET_TIMEOUT_MS;
    private boolean allowRedirects = DEFAULT_ALLOW_REDIRECTS;

    private final Object clientLock = new Object();
    private HttpClient4Client client;

    HttpClient4ClientFactory(
            int maxConnections,
            int maxConnectionsPerRoute,
            int connectionManagerTimeoutMs,
            int connectionTimeoutMs,
            int socketTimeoutMs,
            boolean allowRedirects) {
        this.maxConnections = maxConnections;
        this.maxConnectionsPerRoute = maxConnectionsPerRoute;
        this.connectionManagerTimeoutMs = connectionManagerTimeoutMs;
        this.connectionTimeoutMs = connectionTimeoutMs;
        this.socketTimeoutMs = socketTimeoutMs;
        this.allowRedirects = allowRedirects;
    }

    /**
     * Returns the same instance of {@link HttpClient4Client}, backed by a {@link DefaultHttpClient} instance using a
     * thread-safe pooled connection manager, in response to every request.
     * @param account The Brightpearl customer account a client is required for.
     * @return a {@link HttpClient4Client} instance.
     */
    @Override
    public Client getClient(Account account) {
// Path: src/main/java/uk/co/visalia/brightpearl/apiclient/account/Account.java
// public final class Account implements Serializable {
// 
//     private final Datacenter datacenter;
// 
//     private final String accountCode;
// 
//     /**
//      * Construct an account instance.
//      * @param datacenter the datacenter that hosts the account.
//      * @param accountCode the customer account code.
//      */
//     public Account(Datacenter datacenter, String accountCode) {
//         if (datacenter == null || StringUtils.isBlank(datacenter.getHost()) || StringUtils.isBlank(datacenter.getName())) {
//             throw new IllegalArgumentException("A datacenter name and host must be provided");
//         }
//         if (StringUtils.isBlank(accountCode)) {
//             throw new IllegalArgumentException("An account code must be provided");
//         }
//         this.datacenter = datacenter;
//         this.accountCode = accountCode;
//     }
// 
//     /**
//      * Returns the datacenter
//      * @return the datacenter that hosts the account.
//      */
//     public Datacenter getDatacenter() {
//         return datacenter;
//     }
// 
//     public String getAccountCode() {
//         return accountCode;
//     }
// 
//     @Override
//     public String toString() {
//         return "Account{" +
//                 "datacenter=" + datacenter +
//                 ", accountCode='" + accountCode + '\'' +
//                 '}';
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (o == null || getClass() != o.getClass()) return false;
//         Account account = (Account) o;
//         if (accountCode != null ? !accountCode.equals(account.accountCode) : account.accountCode != null) return false;
//         if (datacenter != null ? !datacenter.equals(account.datacenter) : account.datacenter != null) return false;
//         return true;
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = datacenter != null ? datacenter.hashCode() : 0;
//         result = 31 * result + (accountCode != null ? accountCode.hashCode() : 0);
//         return result;
//     }
// }
// 
// Path: src/main/java/uk/co/visalia/brightpearl/apiclient/http/Client.java
// public interface Client {
// 
//     /**
//      * Executes a request and returns the response. The status code, response body and headers should all be returned if
//      * any response is received from the server, regardless of status code. If the request fails due to a transport error,
//      * a {@link uk.co.visalia.brightpearl.apiclient.exception.BrightpearlHttpException} should be thrown.
//      * @param request The request to be executed.
//      * @return Response from the server.
//      */
//     Response execute(Request request);
// 
// }
// 
// Path: src/main/java/uk/co/visalia/brightpearl/apiclient/http/ClientFactory.java
// public interface ClientFactory {
// 
//     /**
//      * <p>
//      * Get a client to be used for a given {@link uk.co.visalia.brightpearl.apiclient.account.Account}. This method will be called
//      * for every request to be made to the Brightpearl API, so that client instances that support only a single request
//      * are supported. Implementations may return the same instance in response to multiple calls to this method, and
//      * should do so provided the client is thread-safe and supports multiple concurrent requests.
//      * </p><p>
//      * Creating a client for each customer account is not recommended but can be used as a method of throttling the
//      * resources available to each account, so the account is included as a parameter to allow this.
//      * </p>
//      * @param account The Brightpearl customer account a client is required for.
//      * @return A client instance appropriate for the account.
//      */
//     Client getClient(Account account);
// 
// }


// Path: src/main/java/uk/co/visalia/brightpearl/apiclient/http/httpclient4/HttpClient4ClientFactory.java
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import uk.co.visalia.brightpearl.apiclient.account.Account;
import uk.co.visalia.brightpearl.apiclient.http.Client;
import uk.co.visalia.brightpearl.apiclient.http.ClientFactory;

    private int connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
    private int socketTimeoutMs = DEFAULT_SOCKET_TIMEOUT_MS;
    private boolean allowRedirects = DEFAULT_ALLOW_REDIRECTS;

    private final Object clientLock = new Object();
    private HttpClient4Client client;

    HttpClient4ClientFactory(
            int maxConnections,
            int maxConnectionsPerRoute,
            int connectionManagerTimeoutMs,
            int connectionTimeoutMs,
            int socketTimeoutMs,
            boolean allowRedirects) {
        this.maxConnections = maxConnections;
        this.maxConnectionsPerRoute = maxConnectionsPerRoute;
        this.connectionManagerTimeoutMs = connectionManagerTimeoutMs;
        this.connectionTimeoutMs = connectionTimeoutMs;
        this.socketTimeoutMs = socketTimeoutMs;
        this.allowRedirects = allowRedirects;
    }

    /**
     * Returns the same instance of {@link HttpClient4Client}, backed by a {@link DefaultHttpClient} instance using a
     * thread-safe pooled connection manager, in response to every request.
     * @param account The Brightpearl customer account a client is required for.
     * @return a {@link HttpClient4Client} instance.
     */
    @Override
    public Client getClient(Account account) {
        if (client == null) {