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

main.java.com.cloudant.client.api.ClientBuilder Maven / Gradle / Ivy

There is a newer version: 2.20.1
Show newest version
package com.cloudant.client.api;

import com.cloudant.client.api.views.Key;
import com.cloudant.client.internal.util.DeserializationTypes;
import com.cloudant.client.internal.util.IndexDeserializer;
import com.cloudant.client.internal.util.SecurityDeserializer;
import com.cloudant.client.internal.util.ShardDeserializer;
import com.cloudant.client.org.lightcouch.CouchDbException;
import com.cloudant.client.org.lightcouch.CouchDbProperties;
import com.cloudant.http.HttpConnectionInterceptor;
import com.cloudant.http.HttpConnectionRequestInterceptor;
import com.cloudant.http.HttpConnectionResponseInterceptor;
import com.cloudant.http.interceptors.CookieInterceptor;
import com.cloudant.http.interceptors.ProxyAuthInterceptor;
import com.cloudant.http.interceptors.SSLCustomizerInterceptor;
import com.cloudant.http.interceptors.TimeoutCustomizationInterceptor;
import com.google.gson.GsonBuilder;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import javax.net.ssl.SSLSocketFactory;

/**
 * This class builds new {@link CloudantClient} instances.
 *
 * 

Create a new CloudantClient instance for a Cloudant account

*
 * {@code
 * CloudantClient client = ClientBuilder.account("yourCloudantAccount")
 *                          .username("yourUsername")
 *                          .password("yourPassword")
 *                          .build();
 * }
 * 
* *

Create a new CloudantClient instance for a Cloudant Local

*
 * {@code
 * CloudantClient client = ClientBuilder.url(new URL("https://yourCloudantLocalAddress.example"))
 *                          .username("yourUsername")
 *                          .password("yourPassword")
 *                          .build();
 * }
 * 
* *

Examples creating instances with additional options

*

Configure a proxy server

*
 * {@code
 * CloudantClient client = ClientBuilder.account("yourCloudantAccount")
 *                          .username("yourUsername")
 *                          .password("yourPassword")
 *                          .proxyURL(new URL("https://yourProxyServerAddress.example"))
 *                          .proxyUser(yourProxyUser)
 *                          .proxyPassword(yourProxyPass)
 *                          .build();
 * }
 * 
* *

Client with a custom SSL socket factory

*
 * {@code
 * CloudantClient client = ClientBuilder.account("yourCloudantAccount")
 *                          .username("yourUsername")
 *                          .password("yourPassword")
 *                          .customSSLSocketFactory(...)
 *                          .build();
 * }
 * 
* *

Client with custom connection and read timeouts

*
 * {@code
 * CloudantClient client = ClientBuilder.account("yourCloudantAccount")
 *                          .username("yourUsername")
 *                          .password("yourPassword")
 *                          .connectTimeout(1, TimeUnit.MINUTES)
 *                          .readTimeout(1, TimeUnit.MINUTES)
 *                          .build();
 * }
 * 
* * @since 2.0.0 */ public class ClientBuilder { /** * Default max of 6 connections **/ public static final int DEFAULT_MAX_CONNECTIONS = 6; /** * Connection timeout defaults to 5 minutes **/ public static final long DEFAULT_CONNECTION_TIMEOUT = 5l; /** * Read timeout defaults to 5 minutes **/ public static final long DEFAULT_READ_TIMEOUT = 5l; private static final Logger logger = Logger.getLogger(ClientBuilder.class.getName()); private List requestInterceptors = new ArrayList (); private List responseInterceptors = new ArrayList (); private String password; private String username; private URL url; private GsonBuilder gsonBuilder; /** * Defaults to {@link #DEFAULT_MAX_CONNECTIONS} **/ private int maxConnections = DEFAULT_MAX_CONNECTIONS; private URL proxyURL; private String proxyUser; private String proxyPassword; private boolean isSSLAuthenticationDisabled; private SSLSocketFactory authenticatedModeSSLSocketFactory; private long connectTimeout = DEFAULT_CONNECTION_TIMEOUT; private TimeUnit connectTimeoutUnit = TimeUnit.MINUTES; private long readTimeout = DEFAULT_READ_TIMEOUT; private TimeUnit readTimeoutUnit = TimeUnit.MINUTES; /** * Constructs a new ClientBuilder for building a CloudantClient instance to connect to the * Cloudant server with the specified account. * * @param account the Cloudant account name to connect to e.g. "example" is the account name * for the "example.cloudant.com" endpoint * @return a new ClientBuilder for the account * @throws IllegalArgumentException if the specified account name forms an invalid endpoint URL */ public static ClientBuilder account(String account) { logger.config("Account: " + account); try { URL url = new URL(String.format("https://%s.cloudant.com", account)); return ClientBuilder.url(url); } catch (MalformedURLException e) { throw new IllegalArgumentException("Could not generate url from account name.", e); } } /** * Constructs a new ClientBuilder for building a CloudantClient instance to connect to the * Cloudant server with the specified URL. * * @param url server URL e.g. "https://yourCloudantLocalAddress.example" * @return a new ClientBuilder for the account */ public static ClientBuilder url(URL url) { return new ClientBuilder(url); } private ClientBuilder(URL url) { logger.config("URL: " + url); String urlProtocol = url.getProtocol(); String urlHost = url.getHost(); //Check if port exists int urlPort = url.getPort(); if (urlPort < 0) { urlPort = url.getDefaultPort(); } if (url.getUserInfo() != null) { //Get username and password and replace credential variables this.username = url.getUserInfo().substring(0, url .getUserInfo() .indexOf(":")); this.password = url.getUserInfo().substring(url .getUserInfo() .indexOf(":") + 1); } try { //Remove user credentials from url this.url = new URL(urlProtocol + "://" + urlHost + ":" + urlPort); } catch (MalformedURLException e) { throw new RuntimeException(e); } } /** * Build the {@link CloudantClient} instance based on the endpoint used to construct this * client builder and the options that have been set on it before calling this method. * * @return the {@link CloudantClient} instance for the specified end point and options */ public CloudantClient build() { logger.config("Building client using URL: " + url); //Build properties and couchdb client CouchDbProperties props = new CouchDbProperties(url); //Create cookie interceptor if (this.username != null && this.password != null) { //make interceptor if both username and password are not null //Create cookie interceptor and set in HttpConnection interceptors CookieInterceptor cookieInterceptor = new CookieInterceptor(username, password); props.addRequestInterceptors(cookieInterceptor); props.addResponseInterceptors(cookieInterceptor); logger.config("Added cookie interceptor"); } else { //If username or password is null, throw an exception if (username != null || password != null) { //Username and password both have to contain values throw new CouchDbException("Either a username and password must be provided, or " + "both values must be null. Please check the credentials and try again."); } } //If setter methods for read and connection timeout are not called, default values are used. logger.config(String.format("Connect timeout: %s %s", connectTimeout, connectTimeoutUnit)); logger.config(String.format("Read timeout: %s %s", readTimeout, readTimeoutUnit)); props.addRequestInterceptors(new TimeoutCustomizationInterceptor(connectTimeout, connectTimeoutUnit, readTimeout, readTimeoutUnit)); //Set connect options props.setMaxConnections(maxConnections); props.setProxyURL(proxyURL); if (proxyUser != null) { //if there was proxy auth information create an interceptor for it props.addRequestInterceptors(new ProxyAuthInterceptor(proxyUser, proxyPassword)); logger.config("Added proxy auth interceptor"); } if (isSSLAuthenticationDisabled) { props.addRequestInterceptors(SSLCustomizerInterceptor .SSL_AUTH_DISABLED_INTERCEPTOR); logger.config("SSL authentication is disabled"); } if (authenticatedModeSSLSocketFactory != null) { props.addRequestInterceptors(new SSLCustomizerInterceptor( authenticatedModeSSLSocketFactory )); logger.config("Added custom SSL socket factory"); } //Set http connection interceptors if (requestInterceptors != null) { for (HttpConnectionRequestInterceptor requestInterceptor : requestInterceptors) { props.addRequestInterceptors(requestInterceptor); logger.config("Added request interceptor: " + requestInterceptor.getClass() .getName()); } } if (responseInterceptors != null) { for (HttpConnectionResponseInterceptor responseInterceptor : responseInterceptors) { props.addResponseInterceptors(responseInterceptor); logger.config("Added response interceptor: " + responseInterceptor.getClass() .getName()); } } //if no gsonBuilder has been provided, create a new one if (gsonBuilder == null) { gsonBuilder = new GsonBuilder(); logger.config("Using default GSON builder"); } else { logger.config("Using custom GSON builder"); } //always register additional TypeAdapaters for derserializing some Cloudant specific // types before constructing the CloudantClient gsonBuilder.registerTypeAdapter(DeserializationTypes.SHARDS, new ShardDeserializer()) .registerTypeAdapter(DeserializationTypes.INDICES, new IndexDeserializer()) .registerTypeAdapter(DeserializationTypes.PERMISSIONS_MAP, new SecurityDeserializer()) .registerTypeAdapter(Key.ComplexKey.class, new Key.ComplexKeyDeserializer()); return new CloudantClient(props, gsonBuilder); } /** * Sets a username or API key for the client connection. * * @param username the user or API key for the session * @return this ClientBuilder object for setting additional options */ public ClientBuilder username(String username) { this.username = username; return this; } /** * Sets the password for the client connection. The password is the one for the username or * API key set by the {@link #username(String)} method. * * @param password user password or API key passphrase * @return this ClientBuilder object for setting additional options */ public ClientBuilder password(String password) { this.password = password; return this; } /** * Set a custom GsonBuilder to use when serializing and de-serializing JSON in requests and * responses between the CloudantClient and the server. *

* Note: the supplied GsonBuilder will be augmented with some internal TypeAdapters. *

* * @param gsonBuilder the custom GsonBuilder to use * @return this ClientBuilder object for setting additional options */ public ClientBuilder gsonBuilder(GsonBuilder gsonBuilder) { this.gsonBuilder = gsonBuilder; return this; } /** * Set the maximum number of connections to maintain in the connection pool. *

* Note: this setting only applies if using the optional OkHttp dependency. If OkHttp is not * present then the JVM configuration is used for pooling. Consult the JVM documentation for * the {@code http.maxConnections} property for further details. *

* Defaults to {@link #DEFAULT_MAX_CONNECTIONS} * * @param maxConnections the maximum number of simultaneous connections to open to the server * @return this ClientBuilder object for setting additional options */ public ClientBuilder maxConnections(int maxConnections) { this.maxConnections = maxConnections; return this; } /** * Sets a proxy url for the client connection. * * @param proxyURL the URL of the proxy server * @return this ClientBuilder object for setting additional options */ public ClientBuilder proxyURL(URL proxyURL) { this.proxyURL = proxyURL; return this; } /** * Sets an optional proxy username for the client connection. * * @param proxyUser username for the proxy server * @return this ClientBuilder object for setting additional options */ public ClientBuilder proxyUser(String proxyUser) { this.proxyUser = proxyUser; return this; } /** * Sets an optional proxy password for the proxy user specified by * {@link #proxyUser(String)}. * * @param proxyPassword password for the proxy server user * @return this ClientBuilder object for setting additional options */ public ClientBuilder proxyPassword(String proxyPassword) { this.proxyPassword = proxyPassword; return this; } /** * Flag to disable hostname verification and certificate chain validation. This is not * recommended, but for example could be useful for testing with a self-signed certificate. *

* The SSL authentication is enabled by default meaning that hostname verification * and certificate chain validation is done using the JVM default settings. *

* * @return this ClientBuilder object for setting additional options * @throws IllegalStateException if {@link #customSSLSocketFactory(SSLSocketFactory)} * has been called on this ClientBuilder * @see #customSSLSocketFactory(SSLSocketFactory) */ public ClientBuilder disableSSLAuthentication() { if (authenticatedModeSSLSocketFactory == null) { this.isSSLAuthenticationDisabled = true; } else { throw new IllegalStateException("Cannot disable SSL authentication when a " + "custom SSLSocketFactory has been set."); } return this; } /** * Specifies the custom SSLSocketFactory to use when connecting to Cloudant over a * https URL, when SSL authentication is enabled. * * @param factory An SSLSocketFactory, or null for the * default SSLSocketFactory of the JRE. * @return this ClientBuilder object for setting additional options * @throws IllegalStateException if {@link #disableSSLAuthentication()} * has been called on this ClientBuilder * @see #disableSSLAuthentication() */ public ClientBuilder customSSLSocketFactory(SSLSocketFactory factory) { if (!isSSLAuthenticationDisabled) { this.authenticatedModeSSLSocketFactory = factory; } else { throw new IllegalStateException("Cannot use a custom SSLSocketFactory when " + "SSL authentication is disabled."); } return this; } /** * This method adds {@link HttpConnectionInterceptor}s to be used on the CloudantClient * connection. Interceptors can be used to modify the HTTP requests and responses between the * CloudantClient and the server. *

* An example interceptor use might be to apply a custom authorization mechanism. For * instance to use BasicAuth instead of CookieAuth it is possible to use a * {@link com.cloudant.http.interceptors.BasicAuthInterceptor} that adds the BasicAuth * {@code Authorization} header to the request: *

*
     * {@code
     * CloudantClient client = ClientBuilder.account("yourCloudantAccount")
     *      .interceptors(new BasicAuthInterceptor("yourUsername:yourPassword"))
     *      .build()
     * }
     * 
* * @param interceptors one or more HttpConnectionInterceptor objects * @return this ClientBuilder object for setting additional options * @see HttpConnectionInterceptor */ public ClientBuilder interceptors(HttpConnectionInterceptor... interceptors) { for (HttpConnectionInterceptor interceptor : interceptors) { if (interceptor instanceof HttpConnectionRequestInterceptor) { requestInterceptors.add((HttpConnectionRequestInterceptor) interceptor); } if (interceptor instanceof HttpConnectionResponseInterceptor) { responseInterceptors.add((HttpConnectionResponseInterceptor) interceptor); } } return this; } /** * Sets the specified timeout value when opening the client connection. If the timeout * expires before the connection can be established, a * {@link java.net.SocketTimeoutException} is raised. *

* Example creating a {@link CloudantClient} with a connection timeout of 2 seconds: *

*
     * {@code
     * CloudantClient client = ClientBuilder.account("yourCloudantAccount")
     *      .username("yourUsername")
     *      .password("yourPassword")
     *      .connectTimeout(2, TimeUnit.SECONDS)
     *      .build();
     * }
     * 
* Defaults to {@link #DEFAULT_CONNECTION_TIMEOUT} with {@link TimeUnit#MINUTES}. * * @param connectTimeout duration of the read timeout * @param connectTimeoutUnit unit of measurement of the read timeout parameter * @return this ClientBuilder object for setting additional options * @see java.net.HttpURLConnection#setConnectTimeout(int) **/ public ClientBuilder connectTimeout(long connectTimeout, TimeUnit connectTimeoutUnit) { this.connectTimeout = connectTimeout; this.connectTimeoutUnit = connectTimeoutUnit; return this; } /** * Sets the specified timeout value when reading from a {@link java.io.InputStream} with an * established client connection. If the timeout expires before there is data available for * read, a {@link java.net.SocketTimeoutException} is raised. *

* Example creating a {@link CloudantClient} with a read timeout of 2 seconds: *

*
     * {@code
     * CloudantClient client = ClientBuilder.account("yourCloudantAccount")
     *      .username("yourUsername")
     *      .password("yourPassword")
     *      .readTimeout(2, TimeUnit.SECONDS)
     *      .build();
     * }
     * 
* Defaults to {@link #DEFAULT_READ_TIMEOUT} with {@link TimeUnit#MINUTES}. * * @param readTimeout duration of the read timeout * @param readTimeoutUnit unit of measurement of the read timeout parameter * @return this ClientBuilder object for setting additional options * @see java.net.HttpURLConnection#setReadTimeout(int) **/ public ClientBuilder readTimeout(long readTimeout, TimeUnit readTimeoutUnit) { this.readTimeout = readTimeout; this.readTimeoutUnit = readTimeoutUnit; return this; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy