You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 00:08:36 UTC
[47/51] [partial] incubator-juneau git commit: Initial Juno contents
from IBM JazzHub repo
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/RestClient.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/RestClient.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/RestClient.java
new file mode 100755
index 0000000..ffcecbd
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/RestClient.java
@@ -0,0 +1,1411 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client;
+
+import static com.ibm.juno.core.utils.ThrowableUtils.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.lang.reflect.Proxy;
+import java.net.*;
+import java.security.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+import javax.net.ssl.*;
+
+import org.apache.http.*;
+import org.apache.http.auth.*;
+import org.apache.http.client.*;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.config.*;
+import org.apache.http.client.entity.*;
+import org.apache.http.client.methods.*;
+import org.apache.http.config.*;
+import org.apache.http.conn.*;
+import org.apache.http.conn.routing.*;
+import org.apache.http.conn.socket.*;
+import org.apache.http.conn.ssl.*;
+import org.apache.http.conn.util.*;
+import org.apache.http.cookie.*;
+import org.apache.http.entity.*;
+import org.apache.http.impl.client.*;
+import org.apache.http.impl.conn.*;
+import org.apache.http.protocol.*;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.json.*;
+import com.ibm.juno.core.parser.*;
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.urlencoding.*;
+import com.ibm.juno.core.utils.*;
+
+/**
+ * Utility class for interfacing with remote REST interfaces.
+ *
+ *
+ * <h6 class='topic'>Features</h6>
+ * <ul>
+ * <li>Convert POJOs directly to HTTP request message bodies using {@link Serializer} class.
+ * <li>Convert HTTP response message bodies directly to POJOs using {@link Parser} class.
+ * <li>Fluent interface.
+ * <li>Thread safe.
+ * <li>API for interacting with remoteable services.
+ * </ul>
+ *
+ *
+ * <h6 class='topic'>Additional Information</h6>
+ * <ul>
+ * <li><a class='doclink' href='package-summary.html#RestClient'>com.ibm.juno.client > REST client API</a> for more information and code examples.
+ * </ul>
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public class RestClient extends CoreApi {
+
+ Map<String,Object> headers = new TreeMap<String,Object>(String.CASE_INSENSITIVE_ORDER);
+ volatile CloseableHttpClient httpClient;
+ HttpClientConnectionManager httpClientConnectionManager;
+ Serializer<?> serializer;
+ UrlEncodingSerializer urlEncodingSerializer = new UrlEncodingSerializer(); // Used for form posts only.
+ Parser<?> parser;
+ String accept, contentType;
+ List<RestCallInterceptor> interceptors = new ArrayList<RestCallInterceptor>();
+ String remoteableServletUri;
+ private Map<Method,String> remoteableServiceUriMap = new ConcurrentHashMap<Method,String>();
+ private String rootUrl;
+ private SSLOpts sslOpts;
+ private boolean pooled;
+ private volatile boolean isClosed = false;
+ private StackTraceElement[] creationStack;
+
+ /**
+ * The {@link HttpClientBuilder} returned by {@link #createHttpClientBuilder()}.
+ */
+ protected HttpClientBuilder httpClientBuilder;
+
+ /**
+ * Create a new client with no serializer, parser, or HTTP client.
+ * <p>
+ * If you do not specify an {@link HttpClient} via the {@link #setHttpClient(CloseableHttpClient)}, one
+ * will be created using the {@link #createHttpClient()} method.
+ */
+ public RestClient() {
+ httpClientBuilder = createHttpClientBuilder();
+ if (Boolean.getBoolean("com.ibm.juno.client.RestClient.trackCreation"))
+ creationStack = Thread.currentThread().getStackTrace();
+ }
+
+ /**
+ * Create a new client with the specified HTTP client.
+ * <p>
+ * Equivalent to calling the following:
+ * <p class='bcode'>
+ * RestClient rc = <jk>new</jk> RestClient().setHttpClient(httpClient);
+ * </p>
+ *
+ * @param httpClient The HTTP client to use for communicating with remote server.
+ */
+ public RestClient(CloseableHttpClient httpClient) {
+ this();
+ setHttpClient(httpClient);
+ }
+
+ /**
+ * Create a new client with the specified serializer and parser instances.
+ * <p>
+ * Equivalent to calling the following:
+ * <p class='bcode'>
+ * RestClient rc = <jk>new</jk> RestClient().setSerializer(s).setParser(p);
+ * </p>
+ * <p>
+ * If you do not specify an {@link HttpClient} via the {@link #setHttpClient(CloseableHttpClient)}, one
+ * will be created using the {@link #createHttpClient()} method.
+ *
+ * @param s The serializer for converting POJOs to HTTP request message body text.
+ * @param p The parser for converting HTTP response message body text to POJOs.
+ */
+ public RestClient(Serializer<?> s, Parser<?> p) {
+ this();
+ setSerializer(s);
+ setParser(p);
+ }
+
+ /**
+ * Create a new client with the specified serializer and parser instances.
+ * <p>
+ * Equivalent to calling the following:
+ * <p class='bcode'>
+ * RestClient rc = <jk>new</jk> RestClient().setHttpClient(httpClient).setSerializer(s).setParser(p);
+ * </p>
+ *
+ * @param httpClient The HTTP client to use for communicating with remote server.
+ * @param s The serializer for converting POJOs to HTTP request message body text.
+ * @param p The parser for converting HTTP response message body text to POJOs.
+ */
+ public RestClient(CloseableHttpClient httpClient, Serializer<?> s, Parser<?> p) {
+ this();
+ setHttpClient(httpClient);
+ setSerializer(s);
+ setParser(p);
+ }
+
+ /**
+ * Create a new client with the specified serializer and parser classes.
+ * <p>
+ * Equivalent to calling the following:
+ * <p class='bcode'>
+ * RestClient rc = <jk>new</jk> RestClient().setSerializer(s).setParser(p);
+ * </p>
+ * <p>
+ * If you do not specify an {@link HttpClient} via the {@link #setHttpClient(CloseableHttpClient)}, one
+ * will be created using the {@link #createHttpClient()} method.
+ *
+ * @param s The serializer for converting POJOs to HTTP request message body text.
+ * @param p The parser for converting HTTP response message body text to POJOs.
+ * @throws InstantiationException If serializer or parser could not be instantiated.
+ */
+ public RestClient(Class<? extends Serializer<?>> s, Class<? extends Parser<?>> p) throws InstantiationException {
+ this();
+ setSerializer(s);
+ setParser(p);
+ }
+
+ /**
+ * Create a new client with the specified serializer and parser classes.
+ * <p>
+ * Equivalent to calling the following:
+ * <p class='bcode'>
+ * RestClient rc = <jk>new</jk> RestClient().setHttpClient(httpClient).setSerializer(s).setParser(p);
+ * </p>
+ *
+ * @param httpClient The HTTP client to use for communicating with remote server.
+ * @param s The serializer for converting POJOs to HTTP request message body text.
+ * @param p The parser for converting HTTP response message body text to POJOs.
+ * @throws InstantiationException If serializer or parser could not be instantiated.
+ */
+ public RestClient(CloseableHttpClient httpClient, Class<? extends Serializer<?>> s, Class<? extends Parser<?>> p) throws InstantiationException {
+ this();
+ setHttpClient(httpClient);
+ setSerializer(s);
+ setParser(p);
+ }
+
+ /**
+ * Creates an instance of an {@link HttpClient} to be used to handle all HTTP communications with the target server.
+ * <p>
+ * This HTTP client is used when the HTTP client is not specified through one of the constructors or the
+ * {@link #setHttpClient(CloseableHttpClient)} method.
+ * <p>
+ * Subclasses can override this method to provide specially-configured HTTP clients to handle
+ * stuff such as SSL/TLS certificate handling, authentication, etc.
+ * <p>
+ * The default implementation returns an instance of {@link HttpClient} using the client builder
+ * returned by {@link #createHttpClientBuilder()}.
+ *
+ * @return The HTTP client to use.
+ * @throws Exception
+ */
+ protected CloseableHttpClient createHttpClient() throws Exception {
+ // Don't call createConnectionManager() if RestClient.setConnectionManager() was called.
+ if (httpClientConnectionManager == null)
+ httpClientBuilder.setConnectionManager(createConnectionManager());
+ return httpClientBuilder.build();
+ }
+
+ /**
+ * Creates an instance of an {@link HttpClientBuilder} to be used to create
+ * the {@link HttpClient}.
+ * <p>
+ * Subclasses can override this method to provide their own client builder.
+ * </p>
+ * <p>
+ * The predefined method returns an {@link HttpClientBuilder} with the following settings:
+ * </p>
+ * <ul>
+ * <li>Lax redirect strategy.
+ * <li>The connection manager returned by {@link #createConnectionManager()}.
+ * </ul>
+ *
+ * @return The HTTP client builder to use to create the HTTP client.
+ */
+ protected HttpClientBuilder createHttpClientBuilder() {
+ HttpClientBuilder b = HttpClientBuilder.create();
+ b.setRedirectStrategy(new AllowAllRedirects());
+ return b;
+ }
+
+ /**
+ * Creates the {@link HttpClientConnectionManager} returned by {@link #createConnectionManager()}.
+ * <p>
+ * Subclasses can override this method to provide their own connection manager.
+ * </p>
+ * <p>
+ * The default implementation returns an instance of a {@link PoolingHttpClientConnectionManager}.
+ * </p>
+ *
+ * @return The HTTP client builder to use to create the HTTP client.
+ */
+ @SuppressWarnings("resource")
+ protected HttpClientConnectionManager createConnectionManager() {
+ if (sslOpts != null) {
+ HostnameVerifier hv = null;
+ switch (sslOpts.getHostVerify()) {
+ case LAX: hv = new NoopHostnameVerifier(); break;
+ case DEFAULT: hv = new DefaultHostnameVerifier(); break;
+ }
+
+ for (String p : StringUtils.split(sslOpts.getProtocols(), ',')) {
+ try {
+ TrustManager tm = new SimpleX509TrustManager(sslOpts.getCertValidate() == SSLOpts.CertValidate.LAX);
+
+ SSLContext ctx = SSLContext.getInstance(p);
+ ctx.init(null, new TrustManager[] { tm }, null);
+
+ // Create a socket to ensure this algorithm is acceptable.
+ // This will correctly disallow certain configurations (such as SSL_TLS under FIPS)
+ ctx.getSocketFactory().createSocket().close();
+ SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(ctx, hv);
+ setSSLSocketFactory(sf);
+
+ Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sf).build();
+
+ return (pooled ? new PoolingHttpClientConnectionManager(r) : new BasicHttpClientConnectionManager(r));
+ } catch (Throwable t) {}
+ }
+ }
+
+ // Using pooling connection so that this client is threadsafe.
+ return (pooled ? new PoolingHttpClientConnectionManager() : new BasicHttpClientConnectionManager());
+ }
+
+ /**
+ * Set up this client to use BASIC auth.
+ *
+ * @param host The auth scope hostname.
+ * @param port The auth scope port.
+ * @param user The username.
+ * @param pw The password.
+ * @return This object (for method chaining).
+ */
+ public RestClient setBasicAuth(String host, int port, String user, String pw) {
+ AuthScope scope = new AuthScope(host, port);
+ Credentials up = new UsernamePasswordCredentials(user, pw);
+ CredentialsProvider p = new BasicCredentialsProvider();
+ p.setCredentials(scope, up);
+ setDefaultCredentialsProvider(p);
+ return this;
+ }
+
+ /**
+ * When called, the {@link #createConnectionManager()} method will return a {@link PoolingHttpClientConnectionManager}
+ * instead of a {@link BasicHttpClientConnectionManager}.
+ *
+ * @return This object (for method chaining).
+ */
+ public RestClient setPooled() {
+ this.pooled = true;
+ return this;
+ }
+
+ /**
+ * Calls {@link CloseableHttpClient#close()} on the underlying {@link CloseableHttpClient}.
+ * It's good practice to call this method after the client is no longer used.
+ *
+ * @throws IOException
+ */
+ public void close() throws IOException {
+ isClosed = true;
+ if (httpClient != null)
+ httpClient.close();
+ }
+
+ /**
+ * Same as {@link #close()}, but ignores any exceptions.
+ */
+ public void closeQuietly() {
+ isClosed = true;
+ try {
+ if (httpClient != null)
+ httpClient.close();
+ } catch (Throwable t) {}
+ }
+
+ /**
+ * Specifies a request header property to add to all requests created by this client.
+ *
+ * @param name The HTTP header name.
+ * @param value The HTTP header value.
+ * @return This object (for method chaining).
+ */
+ public RestClient setHeader(String name, Object value) {
+ this.headers.put(name, value);
+ return this;
+ }
+
+ /**
+ * Sets the serializer used for serializing POJOs to the HTTP request message body.
+ *
+ * @param serializer The serializer.
+ * @return This object (for method chaining).
+ */
+ public RestClient setSerializer(Serializer<?> serializer) {
+ this.serializer = serializer;
+ return this;
+ }
+
+ /**
+ * Same as {@link #setSerializer(Serializer)}, except takes in a serializer class that
+ * will be instantiated through a no-arg constructor.
+ *
+ * @param c The serializer class.
+ * @return This object (for method chaining).
+ * @throws InstantiationException If serializer could not be instantiated.
+ */
+ public RestClient setSerializer(Class<? extends Serializer<?>> c) throws InstantiationException {
+ try {
+ return setSerializer(c.newInstance());
+ } catch (IllegalAccessException e) {
+ throw new InstantiationException(e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Sets the parser used for parsing POJOs from the HTTP response message body.
+ *
+ * @param parser The parser.
+ * @return This object (for method chaining).
+ */
+ public RestClient setParser(Parser<?> parser) {
+ this.parser = parser;
+ this.accept = parser.getMediaTypes()[0];
+ return this;
+ }
+
+ /**
+ * Same as {@link #setParser(Parser)}, except takes in a parser class that
+ * will be instantiated through a no-arg constructor.
+ *
+ * @param c The parser class.
+ * @return This object (for method chaining).
+ * @throws InstantiationException If parser could not be instantiated.
+ */
+ public RestClient setParser(Class<? extends Parser<?>> c) throws InstantiationException {
+ try {
+ return setParser(c.newInstance());
+ } catch (IllegalAccessException e) {
+ throw new InstantiationException(e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Sets the internal {@link HttpClient} to use for handling HTTP communications.
+ *
+ * @param httpClient The HTTP client.
+ * @return This object (for method chaining).
+ */
+ public RestClient setHttpClient(CloseableHttpClient httpClient) {
+ this.httpClient = httpClient;
+ return this;
+ }
+
+ /**
+ * Adds an interceptor that gets called immediately after a connection is made.
+ *
+ * @param interceptor The interceptor.
+ * @return This object (for method chaining).
+ */
+ public RestClient addInterceptor(RestCallInterceptor interceptor) {
+ interceptors.add(interceptor);
+ return this;
+ }
+
+ /**
+ * Adds a {@link RestCallLogger} to the list of interceptors on this class.
+ *
+ * @param level The log level to log messsages at.
+ * @param log The logger to log messages to.
+ * @return This object (for method chaining).
+ */
+ public RestClient logTo(Level level, Logger log) {
+ addInterceptor(new RestCallLogger(level, log));
+ return this;
+ }
+
+ /**
+ * Returns the serializer currently associated with this client.
+ *
+ * @return The serializer currently associated with this client, or <jk>null</jk> if no serializer is currently associated.
+ */
+ public Serializer<?> getSerializer() {
+ return serializer;
+ }
+
+ /**
+ * Returns the parser currently associated with this client.
+ *
+ * @return The parser currently associated with this client, or <jk>null</jk> if no parser is currently associated.
+ */
+ public Parser<?> getParser() {
+ return parser;
+ }
+
+ /**
+ * Returns the {@link HttpClient} currently associated with this client.
+ *
+ * @return The HTTP client currently associated with this client.
+ * @throws Exception
+ */
+ public HttpClient getHttpClient() throws Exception {
+ if (httpClient == null)
+ httpClient = createHttpClient();
+ return httpClient;
+ }
+
+ /**
+ * Execute the specified request.
+ * Subclasses can override this method to provide specialized handling.
+ *
+ * @param req The HTTP request.
+ * @return The HTTP response.
+ * @throws Exception
+ */
+ protected HttpResponse execute(HttpUriRequest req) throws Exception {
+ return getHttpClient().execute(req);
+ }
+
+ /**
+ * Sets the value for the <code>Accept</code> request header.
+ * <p>
+ * This overrides the media type specified on the parser, but is overridden by calling <code>setHeader(<js>"Accept"</js>, newvalue);</code>
+ *
+ * @param accept The new header value.
+ * @return This object (for method chaining).
+ */
+ public RestClient setAccept(String accept) {
+ this.accept = accept;
+ return this;
+ }
+
+ /**
+ * Sets the value for the <code>Content-Type</code> request header.
+ * <p>
+ * This overrides the media type specified on the serializer, but is overridden by calling <code>setHeader(<js>"Content-Type"</js>, newvalue);</code>
+ *
+ * @param contentType The new header value.
+ * @return This object (for method chaining).
+ */
+ public RestClient setContentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+ /**
+ * Sets the URI of the remoteable services REST servlet for invoking remoteable services.
+ *
+ * @param remoteableServletUri The URI of the REST resource implementing a remoteable services servlet.
+ * (typically an instance of <code>RemoteableServlet</code>).
+ * @return This object (for method chaining).
+ */
+ public RestClient setRemoteableServletUri(String remoteableServletUri) {
+ this.remoteableServletUri = remoteableServletUri;
+ return this;
+ }
+
+ /**
+ * Set a root URL for this client.
+ * <p>
+ * When set, URL strings passed in through the various rest call methods (e.g. {@link #doGet(Object)}
+ * will be prefixed with the specified root.
+ * This root URL is ignored on those methods if you pass in a {@link URL}, {@link URI}, or an absolute URL string.
+ *
+ * @param rootUrl The root URL to prefix to relative URL strings. Trailing slashes are trimmed.
+ * @return This object (for method chaining).
+ */
+ public RestClient setRootUrl(String rootUrl) {
+ if (rootUrl.endsWith("/"))
+ rootUrl = rootUrl.replaceAll("\\/$", "");
+ this.rootUrl = rootUrl;
+ return this;
+ }
+
+ /**
+ * Enable SSL support on this client.
+ *
+ * @param opts The SSL configuration options. See {@link SSLOpts} for details.
+ * This method is a no-op if <code>sslConfig</code> is <jk>null</jk>.
+ * @return This object (for method chaining).
+ * @throws KeyStoreException
+ * @throws NoSuchAlgorithmException
+ */
+ public RestClient enableSSL(SSLOpts opts) throws KeyStoreException, NoSuchAlgorithmException {
+ this.sslOpts = opts;
+ return this;
+ }
+
+ /**
+ * Enable LAX SSL support.
+ * <p>
+ * Certificate chain validation and hostname verification is disabled.
+ *
+ * @return This object (for method chaining).
+ * @throws KeyStoreException
+ * @throws NoSuchAlgorithmException
+ */
+ public RestClient enableLaxSSL() throws KeyStoreException, NoSuchAlgorithmException {
+ return enableSSL(SSLOpts.LAX);
+ }
+
+ /**
+ * Perform a <code>GET</code> request against the specified URL.
+ *
+ * @param url The URL of the remote REST resource. Can be any of the following: {@link String}, {@link URI}, {@link URL}.
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException If any authentication errors occurred.
+ */
+ public RestCall doGet(Object url) throws RestCallException {
+ return doCall("GET", url, false);
+ }
+
+ /**
+ * Perform a <code>PUT</code> request against the specified URL.
+ *
+ * @param url The URL of the remote REST resource. Can be any of the following: {@link String}, {@link URI}, {@link URL}.
+ * @param o The object to serialize and transmit to the URL as the body of the request.
+ * Can be of the following types:
+ * <ul>
+ * <li>{@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
+ * <li>{@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
+ * <li>{@link Object} - POJO to be converted to text using the {@link Serializer} registered with the {@link RestClient}.
+ * <li>{@link HttpEntity} - Bypass Juno serialization and pass HttpEntity directly to HttpClient.
+ * </ul>
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException If any authentication errors occurred.
+ */
+ public RestCall doPut(Object url, Object o) throws RestCallException {
+ return doCall("PUT", url, true).setInput(o);
+ }
+
+ /**
+ * Perform a <code>POST</code> request against the specified URL.
+ *
+ * @param url The URL of the remote REST resource. Can be any of the following: {@link String}, {@link URI}, {@link URL}.
+ * @param o The object to serialize and transmit to the URL as the body of the request.
+ * Can be of the following types:
+ * <ul>
+ * <li>{@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
+ * <li>{@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
+ * <li>{@link Object} - POJO to be converted to text using the {@link Serializer} registered with the {@link RestClient}.
+ * <li>{@link HttpEntity} - Bypass Juno serialization and pass HttpEntity directly to HttpClient.
+ * </ul>
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException If any authentication errors occurred.
+ */
+ public RestCall doPost(Object url, Object o) throws RestCallException {
+ return doCall("POST", url, true).setInput(o);
+ }
+
+ /**
+ * Perform a <code>DELETE</code> request against the specified URL.
+ *
+ * @param url The URL of the remote REST resource. Can be any of the following: {@link String}, {@link URI}, {@link URL}.
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException If any authentication errors occurred.
+ */
+ public RestCall doDelete(Object url) throws RestCallException {
+ return doCall("DELETE", url, false);
+ }
+
+ /**
+ * Perform an <code>OPTIONS</code> request against the specified URL.
+ *
+ * @param url The URL of the remote REST resource. Can be any of the following: {@link String}, {@link URI}, {@link URL}.
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException If any authentication errors occurred.
+ */
+ public RestCall doOptions(Object url) throws RestCallException {
+ return doCall("OPTIONS", url, true);
+ }
+
+ /**
+ * Perform a <code>POST</code> request with a content type of <code>application/x-www-form-urlencoded</code> against the specified URL.
+ *
+ * @param url The URL of the remote REST resource. Can be any of the following: {@link String}, {@link URI}, {@link URL}.
+ * @param o The object to serialize and transmit to the URL as the body of the request, serialized as a form post
+ * using the {@link UrlEncodingSerializer#DEFAULT} serializer.
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException If any authentication errors occurred.
+ */
+ public RestCall doFormPost(Object url, Object o) throws RestCallException {
+ return doCall("POST", url, true)
+ .setInput(new RestRequestEntity(o, urlEncodingSerializer));
+ }
+
+ /**
+ * Performs a REST call where the entire call is specified in a simple string.
+ * <p>
+ * This method is useful for performing callbacks when the target of a callback is passed in
+ * on an initial request, for example to signal when a long-running process has completed.
+ * <p>
+ * The call string can be any of the following formats:
+ * <ul>
+ * <li><js>"[method] [url]"</js> - e.g. <js>"GET http://localhost/callback"</js>
+ * <li><js>"[method] [url] [payload]"</js> - e.g. <js>"POST http://localhost/callback some text payload"</js>
+ * <li><js>"[method] [headers] [url] [payload]"</js> - e.g. <js>"POST {'Content-Type':'text/json'} http://localhost/callback {'some':'json'}"</js>
+ * </ul>
+ * <p>
+ * The payload will always be sent using a simple {@link StringEntity}.
+ *
+ * @param callString The call string.
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException
+ */
+ public RestCall doCallback(String callString) throws RestCallException {
+ String s = callString;
+ try {
+ RestCall rc = null;
+ String method = null, uri = null, content = null;
+ ObjectMap h = null;
+ int i = s.indexOf(' ');
+ if (i != -1) {
+ method = s.substring(0, i).trim();
+ s = s.substring(i).trim();
+ if (s.length() > 0) {
+ if (s.charAt(0) == '{') {
+ i = s.indexOf('}');
+ if (i != -1) {
+ String json = s.substring(0, i+1);
+ h = JsonParser.DEFAULT.parse(json, ObjectMap.class);
+ s = s.substring(i+1).trim();
+ }
+ }
+ if (s.length() > 0) {
+ i = s.indexOf(' ');
+ if (i == -1)
+ uri = s;
+ else {
+ uri = s.substring(0, i).trim();
+ s = s.substring(i).trim();
+ if (s.length() > 0)
+ content = s;
+ }
+ }
+ }
+ }
+ if (method != null && uri != null) {
+ rc = doCall(method, uri, content != null);
+ if (content != null)
+ rc.setInput(new StringEntity(content));
+ if (h != null)
+ for (Map.Entry<String,Object> e : h.entrySet())
+ rc.setHeader(e.getKey(), e.getValue());
+ return rc;
+ }
+ } catch (Exception e) {
+ throw new RestCallException(e);
+ }
+ throw new RestCallException("Invalid format for call string.");
+ }
+
+ /**
+ * Perform a generic REST call.
+ *
+ * @param method The HTTP method.
+ * @param url The URL of the remote REST resource. Can be any of the following: {@link String}, {@link URI}, {@link URL}.
+ * @param content The HTTP body content.
+ * Can be of the following types:
+ * <ul>
+ * <li>{@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
+ * <li>{@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
+ * <li>{@link Object} - POJO to be converted to text using the {@link Serializer} registered with the {@link RestClient}.
+ * <li>{@link HttpEntity} - Bypass Juno serialization and pass HttpEntity directly to HttpClient.
+ * </ul>
+ * This parameter is IGNORED if {@link HttpMethod#hasContent()} is <jk>false</jk>.
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException If any authentication errors occurred.
+ */
+ public RestCall doCall(HttpMethod method, Object url, Object content) throws RestCallException {
+ RestCall rc = doCall(method.name(), url, method.hasContent());
+ if (method.hasContent())
+ rc.setInput(content);
+ return rc;
+ }
+
+ /**
+ * Perform a generic REST call.
+ *
+ * @param method The method name (e.g. <js>"GET"</js>, <js>"OPTIONS"</js>).
+ * @param url The URL of the remote REST resource. Can be any of the following: {@link String}, {@link URI}, {@link URL}.
+ * @param hasContent Boolean flag indicating if the specified request has content associated with it.
+ * @return A {@link RestCall} object that can be further tailored before executing the request
+ * and getting the response as a parsed object.
+ * @throws RestCallException If any authentication errors occurred.
+ */
+ public RestCall doCall(String method, Object url, boolean hasContent) throws RestCallException {
+ HttpRequestBase req = null;
+ RestCall restCall = null;
+ final String methodUC = method.toUpperCase(Locale.ENGLISH);
+ if (hasContent) {
+ req = new HttpEntityEnclosingRequestBase() {
+ @Override /* HttpRequest */
+ public String getMethod() {
+ return methodUC;
+ }
+ };
+ restCall = new RestCall(this, req);
+ if (contentType != null)
+ restCall.setHeader("Content-Type", contentType);
+ } else {
+ req = new HttpRequestBase() {
+ @Override /* HttpRequest */
+ public String getMethod() {
+ return methodUC;
+ }
+ };
+ restCall = new RestCall(this, req);
+ }
+ try {
+ req.setURI(toURI(url));
+ } catch (URISyntaxException e) {
+ throw new RestCallException(e);
+ }
+ if (accept != null)
+ restCall.setHeader("Accept", accept);
+ for (Map.Entry<String,? extends Object> e : headers.entrySet())
+ restCall.setHeader(e.getKey(), e.getValue());
+ return restCall;
+ }
+
+ /**
+ * Create a new proxy interface for the specified remoteable service interface.
+ *
+ * @param interfaceClass The interface to create a proxy for.
+ * @return The new proxy interface.
+ * @throws RuntimeException If the Remotable service URI has not been specified on this
+ * client by calling {@link #setRemoteableServletUri(String)}.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T getRemoteableProxy(final Class<T> interfaceClass) {
+ if (remoteableServletUri == null)
+ throw new RuntimeException("Remoteable service URI has not been specified.");
+ return (T)Proxy.newProxyInstance(
+ interfaceClass.getClassLoader(),
+ new Class[] { interfaceClass },
+ new InvocationHandler() {
+ @Override /* InvocationHandler */
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ try {
+ String uri = remoteableServiceUriMap.get(method);
+ if (uri == null) {
+ // Constructing this string each time can be time consuming, so cache it.
+ uri = remoteableServletUri + '/' + interfaceClass.getName() + '/' + ClassUtils.getMethodSignature(method);
+ remoteableServiceUriMap.put(method, uri);
+ }
+ return doPost(uri, args).getResponse(method.getReturnType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ private Pattern absUrlPattern = Pattern.compile("^\\w+\\:\\/\\/.*");
+
+ private URI toURI(Object url) throws URISyntaxException {
+ assertFieldNotNull(url, "url");
+ if (url instanceof URI)
+ return (URI)url;
+ if (url instanceof URL)
+ ((URL)url).toURI();
+ String s = url.toString();
+ if (rootUrl != null && ! absUrlPattern.matcher(s).matches()) {
+ if (s.isEmpty())
+ s = rootUrl;
+ else {
+ StringBuilder sb = new StringBuilder(rootUrl);
+ if (! s.startsWith("/"))
+ sb.append('/');
+ sb.append(s);
+ s = sb.toString();
+ }
+ }
+ return new URI(s);
+ }
+
+
+ //--------------------------------------------------------------------------------
+ // Overridden methods
+ //--------------------------------------------------------------------------------
+
+ @Override /* CoreAPI */
+ public RestClient setProperty(String property, Object value) throws LockedException {
+ super.setProperty(property, value);
+ if (serializer != null)
+ serializer.setProperty(property, value);
+ if (parser != null)
+ parser.setProperty(property, value);
+ if (urlEncodingSerializer != null)
+ urlEncodingSerializer.setProperty(property, value);
+ return this;
+ }
+
+ @Override /* CoreAPI */
+ public RestClient setProperties(ObjectMap properties) throws LockedException {
+ super.setProperties(properties);
+ if (serializer != null)
+ serializer.setProperties(properties);
+ if (parser != null)
+ parser.setProperties(properties);
+ if (urlEncodingSerializer != null)
+ urlEncodingSerializer.setProperties(properties);
+ return this;
+ }
+
+ @Override /* CoreAPI */
+ public RestClient addNotBeanClasses(Class<?>...classes) throws LockedException {
+ super.addNotBeanClasses(classes);
+ if (serializer != null)
+ serializer.addNotBeanClasses(classes);
+ if (parser != null)
+ parser.addNotBeanClasses(classes);
+ if (urlEncodingSerializer != null)
+ urlEncodingSerializer.addNotBeanClasses(classes);
+ return this;
+ }
+
+ @Override /* CoreAPI */
+ public RestClient addFilters(Class<?>...classes) throws LockedException {
+ super.addFilters(classes);
+ if (serializer != null)
+ serializer.addFilters(classes);
+ if (parser != null)
+ parser.addFilters(classes);
+ if (urlEncodingSerializer != null)
+ urlEncodingSerializer.addFilters(classes);
+ return this;
+ }
+
+ @Override /* CoreAPI */
+ public <T> RestClient addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException {
+ super.addImplClass(interfaceClass, implClass);
+ if (serializer != null)
+ serializer.addImplClass(interfaceClass, implClass);
+ if (parser != null)
+ parser.addImplClass(interfaceClass, implClass);
+ if (urlEncodingSerializer != null)
+ urlEncodingSerializer.addImplClass(interfaceClass, implClass);
+ return this;
+ }
+
+ @Override /* CoreAPI */
+ public RestClient setClassLoader(ClassLoader classLoader) throws LockedException {
+ super.setClassLoader(classLoader);
+ if (serializer != null)
+ serializer.setClassLoader(classLoader);
+ if (parser != null)
+ parser.setClassLoader(classLoader);
+ if (urlEncodingSerializer != null)
+ urlEncodingSerializer.setClassLoader(classLoader);
+ return this;
+ }
+
+
+ //------------------------------------------------------------------------------------------------
+ // Passthrough methods for HttpClientBuilder.
+ //------------------------------------------------------------------------------------------------
+
+ /**
+ * @param redirectStrategy
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setRedirectStrategy(RedirectStrategy)
+ */
+ public RestClient setRedirectStrategy(RedirectStrategy redirectStrategy) {
+ httpClientBuilder.setRedirectStrategy(redirectStrategy);
+ return this;
+ }
+
+ /**
+ * @param cookieSpecRegistry
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setDefaultCookieSpecRegistry(Lookup)
+ */
+ public RestClient setDefaultCookieSpecRegistry(Lookup<CookieSpecProvider> cookieSpecRegistry) {
+ httpClientBuilder.setDefaultCookieSpecRegistry(cookieSpecRegistry);
+ return this;
+ }
+
+ /**
+ * @param requestExec
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setRequestExecutor(HttpRequestExecutor)
+ */
+ public RestClient setRequestExecutor(HttpRequestExecutor requestExec) {
+ httpClientBuilder.setRequestExecutor(requestExec);
+ return this;
+ }
+
+ /**
+ * @param hostnameVerifier
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setSSLHostnameVerifier(HostnameVerifier)
+ */
+ public RestClient setSSLHostnameVerifier(HostnameVerifier hostnameVerifier) {
+ httpClientBuilder.setSSLHostnameVerifier(hostnameVerifier);
+ return this;
+ }
+
+ /**
+ * @param publicSuffixMatcher
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setPublicSuffixMatcher(PublicSuffixMatcher)
+ */
+ public RestClient setPublicSuffixMatcher(PublicSuffixMatcher publicSuffixMatcher) {
+ httpClientBuilder.setPublicSuffixMatcher(publicSuffixMatcher);
+ return this;
+ }
+
+ /**
+ * @param sslContext
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setSSLContext(SSLContext)
+ */
+ public RestClient setSSLContext(SSLContext sslContext) {
+ httpClientBuilder.setSSLContext(sslContext);
+ return this;
+ }
+
+ /**
+ * @param sslSocketFactory
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory)
+ */
+ public RestClient setSSLSocketFactory(LayeredConnectionSocketFactory sslSocketFactory) {
+ httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
+ return this;
+ }
+
+ /**
+ * @param maxConnTotal
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setMaxConnTotal(int)
+ */
+ public RestClient setMaxConnTotal(int maxConnTotal) {
+ httpClientBuilder.setMaxConnTotal(maxConnTotal);
+ return this;
+ }
+
+ /**
+ * @param maxConnPerRoute
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setMaxConnPerRoute(int)
+ */
+ public RestClient setMaxConnPerRoute(int maxConnPerRoute) {
+ httpClientBuilder.setMaxConnPerRoute(maxConnPerRoute);
+ return this;
+ }
+
+ /**
+ * @param config
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setDefaultSocketConfig(SocketConfig)
+ */
+ public RestClient setDefaultSocketConfig(SocketConfig config) {
+ httpClientBuilder.setDefaultSocketConfig(config);
+ return this;
+ }
+
+ /**
+ * @param config
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setDefaultConnectionConfig(ConnectionConfig)
+ */
+ public RestClient setDefaultConnectionConfig(ConnectionConfig config) {
+ httpClientBuilder.setDefaultConnectionConfig(config);
+ return this;
+ }
+
+ /**
+ * @param connTimeToLive
+ * @param connTimeToLiveTimeUnit
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setConnectionTimeToLive(long,TimeUnit)
+ */
+ public RestClient setConnectionTimeToLive(long connTimeToLive, TimeUnit connTimeToLiveTimeUnit) {
+ httpClientBuilder.setConnectionTimeToLive(connTimeToLive, connTimeToLiveTimeUnit);
+ return this;
+ }
+
+ /**
+ * @param connManager
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setConnectionManager(HttpClientConnectionManager)
+ */
+ public RestClient setConnectionManager(HttpClientConnectionManager connManager) {
+ this.httpClientConnectionManager = connManager;
+ httpClientBuilder.setConnectionManager(connManager);
+ return this;
+ }
+
+ /**
+ * @param shared
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setConnectionManagerShared(boolean)
+ */
+ public RestClient setConnectionManagerShared(boolean shared) {
+ httpClientBuilder.setConnectionManagerShared(shared);
+ return this;
+ }
+
+ /**
+ * @param reuseStrategy
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setConnectionReuseStrategy(ConnectionReuseStrategy)
+ */
+ public RestClient setConnectionReuseStrategy(ConnectionReuseStrategy reuseStrategy) {
+ httpClientBuilder.setConnectionReuseStrategy(reuseStrategy);
+ return this;
+ }
+
+ /**
+ * @param keepAliveStrategy
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setKeepAliveStrategy(ConnectionKeepAliveStrategy)
+ */
+ public RestClient setKeepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy) {
+ httpClientBuilder.setKeepAliveStrategy(keepAliveStrategy);
+ return this;
+ }
+
+ /**
+ * @param targetAuthStrategy
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setTargetAuthenticationStrategy(AuthenticationStrategy)
+ */
+ public RestClient setTargetAuthenticationStrategy(AuthenticationStrategy targetAuthStrategy) {
+ httpClientBuilder.setTargetAuthenticationStrategy(targetAuthStrategy);
+ return this;
+ }
+
+ /**
+ * @param proxyAuthStrategy
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setProxyAuthenticationStrategy(AuthenticationStrategy)
+ */
+ public RestClient setProxyAuthenticationStrategy(AuthenticationStrategy proxyAuthStrategy) {
+ httpClientBuilder.setProxyAuthenticationStrategy(proxyAuthStrategy);
+ return this;
+ }
+
+ /**
+ * @param userTokenHandler
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setUserTokenHandler(UserTokenHandler)
+ */
+ public RestClient setUserTokenHandler(UserTokenHandler userTokenHandler) {
+ httpClientBuilder.setUserTokenHandler(userTokenHandler);
+ return this;
+ }
+
+ /**
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#disableConnectionState()
+ */
+ public RestClient disableConnectionState() {
+ httpClientBuilder.disableConnectionState();
+ return this;
+ }
+
+ /**
+ * @param schemePortResolver
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setSchemePortResolver(SchemePortResolver)
+ */
+ public RestClient setSchemePortResolver(SchemePortResolver schemePortResolver) {
+ httpClientBuilder.setSchemePortResolver(schemePortResolver);
+ return this;
+ }
+
+ /**
+ * @param userAgent
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setUserAgent(String)
+ */
+ public RestClient setUserAgent(String userAgent) {
+ httpClientBuilder.setUserAgent(userAgent);
+ return this;
+ }
+
+ /**
+ * @param defaultHeaders
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setDefaultHeaders(Collection)
+ */
+ public RestClient setDefaultHeaders(Collection<? extends Header> defaultHeaders) {
+ httpClientBuilder.setDefaultHeaders(defaultHeaders);
+ return this;
+ }
+
+ /**
+ * @param itcp
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#addInterceptorFirst(HttpResponseInterceptor)
+ */
+ public RestClient addInterceptorFirst(HttpResponseInterceptor itcp) {
+ httpClientBuilder.addInterceptorFirst(itcp);
+ return this;
+ }
+
+ /**
+ * @param itcp
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#addInterceptorLast(HttpResponseInterceptor)
+ */
+ public RestClient addInterceptorLast(HttpResponseInterceptor itcp) {
+ httpClientBuilder.addInterceptorLast(itcp);
+ return this;
+ }
+
+ /**
+ * @param itcp
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#addInterceptorFirst(HttpRequestInterceptor)
+ */
+ public RestClient addInterceptorFirst(HttpRequestInterceptor itcp) {
+ httpClientBuilder.addInterceptorFirst(itcp);
+ return this;
+ }
+
+ /**
+ * @param itcp
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#addInterceptorLast(HttpRequestInterceptor)
+ */
+ public RestClient addInterceptorLast(HttpRequestInterceptor itcp) {
+ httpClientBuilder.addInterceptorLast(itcp);
+ return this;
+ }
+
+ /**
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#disableCookieManagement()
+ */
+ public RestClient disableCookieManagement() {
+ httpClientBuilder.disableCookieManagement();
+ return this;
+ }
+
+ /**
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#disableContentCompression()
+ */
+ public RestClient disableContentCompression() {
+ httpClientBuilder.disableContentCompression();
+ return this;
+ }
+
+ /**
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#disableAuthCaching()
+ */
+ public RestClient disableAuthCaching() {
+ httpClientBuilder.disableAuthCaching();
+ return this;
+ }
+
+ /**
+ * @param httpprocessor
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setHttpProcessor(HttpProcessor)
+ */
+ public RestClient setHttpProcessor(HttpProcessor httpprocessor) {
+ httpClientBuilder.setHttpProcessor(httpprocessor);
+ return this;
+ }
+
+ /**
+ * @param retryHandler
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setRetryHandler(HttpRequestRetryHandler)
+ */
+ public RestClient setRetryHandler(HttpRequestRetryHandler retryHandler) {
+ httpClientBuilder.setRetryHandler(retryHandler);
+ return this;
+ }
+
+ /**
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#disableAutomaticRetries()
+ */
+ public RestClient disableAutomaticRetries() {
+ httpClientBuilder.disableAutomaticRetries();
+ return this;
+ }
+
+ /**
+ * @param proxy
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setProxy(HttpHost)
+ */
+ public RestClient setProxy(HttpHost proxy) {
+ httpClientBuilder.setProxy(proxy);
+ return this;
+ }
+
+ /**
+ * @param routePlanner
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setRoutePlanner(HttpRoutePlanner)
+ */
+ public RestClient setRoutePlanner(HttpRoutePlanner routePlanner) {
+ httpClientBuilder.setRoutePlanner(routePlanner);
+ return this;
+ }
+
+ /**
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#disableRedirectHandling()
+ */
+ public RestClient disableRedirectHandling() {
+ httpClientBuilder.disableRedirectHandling();
+ return this;
+ }
+
+ /**
+ * @param connectionBackoffStrategy
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setConnectionBackoffStrategy(ConnectionBackoffStrategy)
+ */
+ public RestClient setConnectionBackoffStrategy(ConnectionBackoffStrategy connectionBackoffStrategy) {
+ httpClientBuilder.setConnectionBackoffStrategy(connectionBackoffStrategy);
+ return this;
+ }
+
+ /**
+ * @param backoffManager
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setBackoffManager(BackoffManager)
+ */
+ public RestClient setBackoffManager(BackoffManager backoffManager) {
+ httpClientBuilder.setBackoffManager(backoffManager);
+ return this;
+ }
+
+ /**
+ * @param serviceUnavailStrategy
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setServiceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy)
+ */
+ public RestClient setServiceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy serviceUnavailStrategy) {
+ httpClientBuilder.setServiceUnavailableRetryStrategy(serviceUnavailStrategy);
+ return this;
+ }
+
+ /**
+ * @param cookieStore
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setDefaultCookieStore(CookieStore)
+ */
+ public RestClient setDefaultCookieStore(CookieStore cookieStore) {
+ httpClientBuilder.setDefaultCookieStore(cookieStore);
+ return this;
+ }
+
+ /**
+ * @param credentialsProvider
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setDefaultCredentialsProvider(CredentialsProvider)
+ */
+ public RestClient setDefaultCredentialsProvider(CredentialsProvider credentialsProvider) {
+ httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
+ return this;
+ }
+
+ /**
+ * @param authSchemeRegistry
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setDefaultAuthSchemeRegistry(Lookup)
+ */
+ public RestClient setDefaultAuthSchemeRegistry(Lookup<AuthSchemeProvider> authSchemeRegistry) {
+ httpClientBuilder.setDefaultAuthSchemeRegistry(authSchemeRegistry);
+ return this;
+ }
+
+ /**
+ * @param contentDecoderMap
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setContentDecoderRegistry(Map)
+ */
+ public RestClient setContentDecoderRegistry(Map<String,InputStreamFactory> contentDecoderMap) {
+ httpClientBuilder.setContentDecoderRegistry(contentDecoderMap);
+ return this;
+ }
+
+ /**
+ * @param config
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#setDefaultRequestConfig(RequestConfig)
+ */
+ public RestClient setDefaultRequestConfig(RequestConfig config) {
+ httpClientBuilder.setDefaultRequestConfig(config);
+ return this;
+ }
+
+ /**
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#useSystemProperties()
+ */
+ public RestClient useSystemProperties() {
+ httpClientBuilder.useSystemProperties();
+ return this;
+ }
+
+ /**
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#evictExpiredConnections()
+ */
+ public RestClient evictExpiredConnections() {
+ httpClientBuilder.evictExpiredConnections();
+ return this;
+ }
+
+ /**
+ * @param maxIdleTime
+ * @param maxIdleTimeUnit
+ * @return This object (for method chaining).
+ * @see HttpClientBuilder#evictIdleConnections(long,TimeUnit)
+ */
+ public RestClient evictIdleConnections(long maxIdleTime, TimeUnit maxIdleTimeUnit) {
+ httpClientBuilder.evictIdleConnections(maxIdleTime, maxIdleTimeUnit);
+ return this;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (! isClosed) {
+ System.err.println("WARNING: RestClient garbage collected before it was finalized.");
+ if (creationStack != null) {
+ System.err.println("Creation Stack:");
+ for (StackTraceElement e : creationStack)
+ System.err.println(e);
+ } else {
+ System.err.println("Creation stack traces can be displayed by setting the system property 'com.ibm.juno.client.RestClient.trackCreation' to true.");
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/RestRequestEntity.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/RestRequestEntity.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/RestRequestEntity.java
new file mode 100755
index 0000000..9336c23
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/RestRequestEntity.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client;
+
+import java.io.*;
+
+import org.apache.http.entity.*;
+import org.apache.http.message.*;
+
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.utils.*;
+
+/**
+ * HttpEntity for serializing POJOs as the body of HTTP requests.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class RestRequestEntity extends BasicHttpEntity {
+ final Object output;
+ final Serializer<?> serializer;
+ byte[] outputBytes;
+
+ /**
+ * Constructor.
+ * @param input The POJO to serialize. Can also be a {@link Reader} or {@link InputStream}.
+ * @param serializer The serializer to use to serialize this response.
+ */
+ public RestRequestEntity(Object input, Serializer<?> serializer) {
+ this.output = input;
+ this.serializer = serializer;
+ if (serializer != null)
+ setContentType(new BasicHeader("Content-Type", serializer.getResponseContentType()));
+ }
+
+ @Override /* BasicHttpEntity */
+ public void writeTo(OutputStream os) throws IOException {
+ if (output instanceof InputStream) {
+ IOPipe.create(output, os).closeOut().run();
+ } else if (output instanceof Reader) {
+ IOPipe.create(output, new OutputStreamWriter(os, IOUtils.UTF8)).closeOut().run();
+ } else {
+ try {
+ if (serializer == null) {
+ // If no serializer specified, just close the stream.
+ os.close();
+ } else if (! serializer.isWriterSerializer()) {
+ OutputStreamSerializer s2 = (OutputStreamSerializer)serializer;
+ s2.serialize(output, os);
+ os.close();
+ } else {
+ Writer w = new OutputStreamWriter(os, IOUtils.UTF8);
+ WriterSerializer s2 = (WriterSerializer)serializer;
+ s2.serialize(output, w);
+ w.close();
+ }
+ } catch (SerializeException e) {
+ throw new com.ibm.juno.client.RestCallException(e);
+ }
+ }
+ }
+
+ @Override /* BasicHttpEntity */
+ public boolean isRepeatable() {
+ return true;
+ }
+
+ @Override /* BasicHttpEntity */
+ public InputStream getContent() {
+ if (outputBytes == null) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ writeTo(baos);
+ outputBytes = baos.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return new ByteArrayInputStream(outputBytes);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/RetryOn.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/RetryOn.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/RetryOn.java
new file mode 100755
index 0000000..6b9bf75
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/RetryOn.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client;
+
+/**
+ * Used to determine whether a request should be retried based on the HTTP response code.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public interface RetryOn {
+
+ /**
+ * Default RetryOn that returns <jk>true</jk> of any HTTP response >= 400 is received.
+ */
+ public static final RetryOn DEFAULT = new RetryOn() {
+ @Override /* RetryOn */
+ public boolean onCode(int httpResponseCode) {
+ return httpResponseCode <= 0 || httpResponseCode >= 400;
+ }
+ };
+
+ /**
+ * Subclasses should override this method to determine whether the HTTP response is retryable.
+ *
+ * @param httpResponseCode The HTTP response code.
+ * @return <jk>true</jk> if the specified response code is retryable.
+ */
+ boolean onCode(int httpResponseCode);
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/SSLOpts.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/SSLOpts.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/SSLOpts.java
new file mode 100755
index 0000000..2a7ed82
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/SSLOpts.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client;
+
+import com.ibm.juno.core.utils.*;
+
+/**
+ * SSL configuration options that get passed to {@link RestClient#enableSSL(SSLOpts)}.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public class SSLOpts {
+
+ private String protocols = getDefaultProtocols();
+ private CertValidate certValidate = CertValidate.DEFAULT;
+ private HostVerify hostVerify = HostVerify.DEFAULT;
+
+ /**
+ * Reusable SSL options for lenient SSL (no cert validation or hostname verification).
+ */
+ public static final SSLOpts LAX = new SSLOpts(null, CertValidate.LAX, HostVerify.LAX);
+
+ /**
+ * Reusable SSL options for normal SSL (default cert validation and hostname verification).
+ */
+ public static final SSLOpts DEFAULT = new SSLOpts(null, CertValidate.DEFAULT, HostVerify.DEFAULT);
+
+ /**
+ * Constructor.
+ */
+ public SSLOpts() {}
+
+ /**
+ * Constructor.
+ *
+ * @param protocols A comma-delimited list of supported SSL protocols.
+ * If <jk>null</jk>, uses the value returned by {@link #getDefaultProtocols()}.
+ * @param certValidate Certificate validation setting.
+ * @param hostVerify Host verification setting.
+ */
+ public SSLOpts(String protocols, CertValidate certValidate, HostVerify hostVerify) {
+ if (protocols != null)
+ this.protocols = protocols;
+ this.certValidate = certValidate;
+ this.hostVerify = hostVerify;
+ }
+
+ /**
+ * Returns the default list of SSL protocols to support when the <code>protocols</code>
+ * parameter on the constructor is <jk>null</jk>.
+ * <p>
+ * The default value is <jk>"SSL_TLS,TLS,SSL"</js> unless overridden by one of the following
+ * system properties:
+ * <ul>
+ * <li><js>"com.ibm.team.repository.transport.client.protocol"</js>
+ * <li><js>"transport.client.protocol"</js>
+ * </ul>
+ * <p>
+ * Subclasses can override this method to provide their own logic for determining default supported protocols.
+ *
+ * @return The comma-delimited list of supported protocols.
+ */
+ protected String getDefaultProtocols() {
+ String sp = System.getProperty("com.ibm.team.repository.transport.client.protocol");
+ if (StringUtils.isEmpty(sp))
+ sp = System.getProperty("transport.client.protocol");
+ if (StringUtils.isEmpty(sp))
+ sp = "SSL_TLS,TLS,SSL";
+ return sp;
+ }
+
+
+ //--------------------------------------------------------------------------------
+ // Bean properties
+ //--------------------------------------------------------------------------------
+
+ /**
+ * Bean property getter: <property>protocols</property>.
+ *
+ * @return The value of the <property>protocols</property> property on this bean, or <jk>null</jk> if it is not set.
+ */
+ public String getProtocols() {
+ return protocols;
+ }
+
+ /**
+ * Bean property setter: <property>protocols</property>.
+ *
+ * @param protocols The new value for the <property>properties</property> property on this bean.
+ * @return This object (for method chaining).
+ */
+ public SSLOpts setProtocols(String protocols) {
+ this.protocols = protocols;
+ return this;
+ }
+
+ /**
+ * Bean property getter: <property>certValidate</property>.
+ *
+ * @return The value of the <property>certValidate</property> property on this bean, or <jk>null</jk> if it is not set.
+ */
+ public CertValidate getCertValidate() {
+ return certValidate;
+ }
+
+ /**
+ * Bean property setter: <property>certValidate</property>.
+ *
+ * @param certValidate The new value for the <property>properties</property> property on this bean.
+ * @return This object (for method chaining).
+ */
+ public SSLOpts setCertValidate(CertValidate certValidate) {
+ this.certValidate = certValidate;
+ return this;
+ }
+
+ /**
+ * Bean property getter: <property>hostVerify</property>.
+ *
+ * @return The value of the <property>hostVerify</property> property on this bean, or <jk>null</jk> if it is not set.
+ */
+ public HostVerify getHostVerify() {
+ return hostVerify;
+ }
+
+ /**
+ * Bean property setter: <property>hostVerify</property>.
+ *
+ * @param hostVerify The new value for the <property>properties</property> property on this bean.
+ * @return This object (for method chaining).
+ */
+ public SSLOpts setHostVerify(HostVerify hostVerify) {
+ this.hostVerify = hostVerify;
+ return this;
+ }
+
+
+ //--------------------------------------------------------------------------------
+ // Enums
+ //--------------------------------------------------------------------------------
+
+ /**
+ * Certificate validation options.
+ * <p>
+ * Used as enum for {@link SSLOpts#getCertValidate()} property.
+ */
+ @SuppressWarnings("hiding")
+ public static enum CertValidate {
+
+ /**
+ * Verify that the certificate is valid, but allow for self-signed certificates.
+ */
+ LAX,
+
+ /**
+ * Do normal certificate chain validation.
+ */
+ DEFAULT
+ }
+
+ /**
+ * Certificate host verification options.
+ * <p>
+ * Used as enum for {@link SSLOpts#getHostVerify()} property.
+ */
+ @SuppressWarnings("hiding")
+ public enum HostVerify {
+
+ /**
+ * Don't verify the hostname in the certificate.
+ */
+ LAX,
+
+ /**
+ * Do normal hostname verification.
+ */
+ DEFAULT
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/SerializedNameValuePair.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/SerializedNameValuePair.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/SerializedNameValuePair.java
new file mode 100755
index 0000000..cdb3b2e
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/SerializedNameValuePair.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client;
+
+import static com.ibm.juno.core.urlencoding.UonSerializerProperties.*;
+
+import org.apache.http.*;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.urlencoding.*;
+
+/**
+ * Subclass of {@link NameValuePair} for serializing POJOs as URL-encoded form post entries
+ * using the {@link UrlEncodingSerializer class}.
+ * <p>
+ * Example:
+ * <p class='bcode'>
+ * NameValuePairs params = <jk>new</jk> NameValuePairs()
+ * .append(<jk>new</jk> SerializedNameValuePair(<js>"myPojo"</js>, pojo, UrlEncodingSerializer.<jsf>DEFAULT_SIMPLE</jsf>))
+ * .append(<jk>new</jk> BasicNameValuePair(<js>"someOtherParam"</js>, <js>"foobar"</js>));
+ * request.setEntity(<jk>new</jk> UrlEncodedFormEntity(params));
+ * </p>
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class SerializedNameValuePair implements NameValuePair {
+ private String name;
+ private Object value;
+ private UrlEncodingSerializer serializer;
+
+ // We must be sure to disable character encoding since it's done in the http client layer.
+ private static final ObjectMap op = new ObjectMap().append(UON_encodeChars, false);
+
+ /**
+ * Constructor.
+ *
+ * @param name The parameter name.
+ * @param value The POJO to serialize to the parameter value.
+ * @param serializer The serializer to use to convert the value to a string.
+ */
+ public SerializedNameValuePair(String name, Object value, UrlEncodingSerializer serializer) {
+ this.name = name;
+ this.value = value;
+ this.serializer = serializer;
+ }
+
+ @Override /* NameValuePair */
+ public String getName() {
+ if (name != null && name.length() > 0) {
+ char c = name.charAt(0);
+ if (c == '$' || c == '(') {
+ try {
+ UonSerializerContext ctx = serializer.createContext(op, null);
+ return serializer.serialize(name, ctx);
+ } catch (SerializeException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return name;
+ }
+
+ @Override /* NameValuePair */
+ public String getValue() {
+ try {
+ UonSerializerContext ctx = serializer.createContext(op, null);
+ return serializer.serialize(value, ctx);
+ } catch (SerializeException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/SimpleX509TrustManager.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/SimpleX509TrustManager.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/SimpleX509TrustManager.java
new file mode 100755
index 0000000..72975cb
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/SimpleX509TrustManager.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client;
+
+import java.security.*;
+import java.security.cert.*;
+
+import javax.net.ssl.*;
+
+/**
+ * A trust manager that optionally allows for self-signed certificates.
+ */
+public final class SimpleX509TrustManager implements X509TrustManager {
+
+ private X509TrustManager baseTrustManager; // The JRE-provided trust manager used to validate certificates presented by a server.
+
+ /**
+ * Constructor.
+ *
+ * @param lax If <jk>true</jk>, allow self-signed and expired certificates.
+ * @throws KeyStoreException
+ * @throws NoSuchAlgorithmException
+ */
+ public SimpleX509TrustManager(boolean lax) throws KeyStoreException, NoSuchAlgorithmException {
+ if (! lax) {
+ // Find the JRE-provided X509 trust manager.
+ KeyStore ks = KeyStore.getInstance("jks");
+ TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init(ks);
+ for (TrustManager tm : factory.getTrustManagers()) {
+ if (tm instanceof X509TrustManager) {
+ baseTrustManager = (X509TrustManager)tm; // Take the first X509TrustManager we find
+ return;
+ }
+ }
+ throw new IllegalStateException("Couldn't find JRE's X509TrustManager"); //$NON-NLS-1$
+ }
+ }
+
+ @Override /* X509TrustManager */
+ public X509Certificate[] getAcceptedIssuers() {
+ return baseTrustManager == null ? new X509Certificate[0] : baseTrustManager.getAcceptedIssuers();
+ }
+
+ @Override /* X509TrustManager */
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ if (baseTrustManager != null)
+ baseTrustManager.checkClientTrusted(chain, authType);
+ }
+
+ @Override /* X509TrustManager */
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ if (baseTrustManager != null)
+ baseTrustManager.checkServerTrusted(chain, authType);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/CertificateStore.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/CertificateStore.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/CertificateStore.java
new file mode 100755
index 0000000..f06eaf5
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/CertificateStore.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+import java.util.*;
+
+/**
+ * Specialized certificate storage based on {@link KeyStore} for managing trusted certificates.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public class CertificateStore {
+
+ private final KeyStore keyStore;
+
+ /**
+ * Get the underlying KeyStore.
+ */
+ KeyStore getKeyStore() {
+ return keyStore;
+ }
+
+ /**
+ * Helper method that creates a {@link KeyStore} by reading it from a file.
+ */
+ static KeyStore load(File file, String password) throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
+ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+ try {
+ InputStream input = new FileInputStream(file);
+ try {
+ ks.load(input, password == null ? null : password.toCharArray());
+ } finally {
+ input.close();
+ }
+ } catch (IOException e) {
+ // Return an empty initialized KeyStore
+ ks.load(null, null);
+ }
+ return ks;
+ }
+
+ /**
+ * Helper method that writes a {@link KeyStore} to a file.
+ */
+ static void store(KeyStore ks, File file, String password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
+ OutputStream output = new FileOutputStream(file);
+ try {
+ ks.store(output, password == null ? null : password.toCharArray());
+ } finally {
+ output.close();
+ }
+ }
+
+ /**
+ * Helper to compute a unique alias within the trust store for a specified certificate.
+ * @param cert The certificate to compute an alias for.
+ */
+ static String computeAlias(Certificate cert) {
+ // There appears to be no standard way to construct certificate aliases,
+ // but this class never depends on looking up a certificate by its
+ // computed alias, so just create an alias that's unique and be done.
+ return UUID.randomUUID().toString();
+ }
+
+ /**
+ * Construct a new TrustStore initially containing no certificates.
+ */
+ public CertificateStore() throws NoSuchAlgorithmException, CertificateException, IOException {
+ try {
+ keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ } catch (KeyStoreException e) {
+ // If the code above caused a KeyStoreException, then the JVM classpath is probably messed up.
+ throw new RuntimeException("KeyStoreException: ["+e.getLocalizedMessage()+"]. "
+ + "Likely cause is that the Java Cryptography Extension libraries are missing from the JRE classpath. "
+ + "Make sure %JAVA_HOME%/lib/ext is specified in your JVM's java.ext.dirs system property.");
+ }
+ keyStore.load(null, null);
+ }
+
+ /**
+ * Does the trust store contain the specified certificate?
+ */
+ public boolean containsCertificate(Certificate cert) throws KeyStoreException {
+ return (keyStore.getCertificateAlias(cert) != null);
+ }
+
+ /**
+ * Enter the specified certificate into the trust store.
+ */
+ public void enterCertificate(Certificate cert) throws KeyStoreException {
+ if (! containsCertificate(cert))
+ keyStore.setCertificateEntry(computeAlias(cert), cert);
+ }
+
+ /*
+ * Helper to copy all the certificate entries, and none of the other
+ * entries, from a {@link KeyStore} into the trust store.
+ */
+ private void enterCertificates(KeyStore ks) throws KeyStoreException {
+ for (Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
+ String alias = e.nextElement();
+ if (ks.isCertificateEntry(alias)) {
+ Certificate cert = ks.getCertificate(alias);
+ enterCertificate(cert);
+ }
+ }
+ }
+
+ /**
+ * Load the specified {@link KeyStore} file and copy all of the certificates
+ * it contains into the trust store. Only certificates, and not any other
+ * entries, are loaded.
+ */
+ public void loadCertificates(File file, String password) throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
+ KeyStore ks = load(file, password);
+ enterCertificates(ks);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ICertificateValidator.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ICertificateValidator.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ICertificateValidator.java
new file mode 100755
index 0000000..0ee07e8
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ICertificateValidator.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.security.cert.*;
+
+/**
+ * Validator of certificates presented by a server when establishing an SSL
+ * connection.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public interface ICertificateValidator {
+
+ /** Action to take for a server-supplied certificate. */
+ public enum Trust {
+
+ /** Do not accept the certificate. */
+ REJECT,
+
+ /** Accept the certificate temporarily for the current connection. */
+ ACCEPT_CONNECTION,
+
+ /** Accept the certificate temporarily for the current session. */
+ ACCEPT_SESSION,
+
+ /** Accept the certificate permanently, by saving it in the user's trust store.*/
+ ACCEPT_PERMANENT
+ }
+
+ /**
+ * There is a problem accepting the server-supplied certificate. What should
+ * be done?
+ *
+ * @param cert The problematic certificate presented by the server
+ * @param problem The {@link CertificateException} that may indicate the specific
+ * problem with the certificate, e.g. {@link CertificateExpiredException}.
+ * @return The disposition on the certificate.
+ */
+ Trust validate(X509Certificate cert, CertificateException problem);
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ITrustStoreProvider.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ITrustStoreProvider.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ITrustStoreProvider.java
new file mode 100755
index 0000000..47256a9
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ITrustStoreProvider.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+
+/**
+ * Utility class for handling certificate stores.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public interface ITrustStoreProvider {
+
+ /**
+ * Returns the store of all certificates trusted for the lifetime
+ * of this trust provider
+ */
+ CertificateStore getSessionTrustStore();
+
+ /**
+ * Returns the store of all permanently trusted certificates.
+ */
+ CertificateStore getRuntimeTrustStore();
+
+ /**
+ * Install a certificate in the user's application-specific on-disk key
+ * store, if possible.
+ */
+ public void installCertificate(Certificate cert) throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/LenientCertificateValidator.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/LenientCertificateValidator.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/LenientCertificateValidator.java
new file mode 100755
index 0000000..a12ca43
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/LenientCertificateValidator.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.security.cert.*;
+
+/**
+ * Lenient certificate validator that always accepts invalid certificates.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public final class LenientCertificateValidator implements ICertificateValidator {
+
+ /** Singleton */
+ public static final ICertificateValidator INSTANCE = new LenientCertificateValidator();
+
+ @Override /* ICertificateValidator */
+ public Trust validate(X509Certificate certificate, CertificateException problem) {
+ return Trust.ACCEPT_CONNECTION;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.java
new file mode 100755
index 0000000..149302d
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+
+/**
+ * Trust store provider with shared static certificate stores.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public final class SharedTrustStoreProvider implements ITrustStoreProvider {
+
+ // In-memory trust store of all certificates explicitly accepted by the
+ // certificate validator during this session. The validator will not be
+ // called again during this session for any of these certificates. These may
+ // include expired, not yet valid, or otherwise untrusted certificates.
+ // These are kept distinctly, rather than merged into the runtime trust
+ // store, because the base trust manager will never accept expired, etc.
+ // certificates, even if from a trusted source.
+ private static CertificateStore sessionCerts;
+
+ // In-memory trust store of all permanently trusted certificates, assembled
+ // from a number of key store files. These are provided to the base trust
+ // manager as the basis for its decision making.
+ private static CertificateStore runtimeCerts;
+
+ // Location and password of the user's private trust store for this application.
+ private static String userTrustStoreLocation;
+ private static String userTrustStorePassword;
+
+ static {
+ init();
+ }
+
+ private static final void init() {
+ try {
+ String userHome = System.getProperty("user.home");
+ String javaHome = System.getProperty("java.home");
+
+ userTrustStoreLocation = userHome + "/.jazzcerts";
+ userTrustStorePassword = "ibmrationaljazz";
+
+ sessionCerts = new CertificateStore();
+
+ runtimeCerts = new CertificateStore();
+
+ // JRE keystore override
+ String file = System.getProperty("javax.net.ssl.trustStore");
+ String password = System.getProperty("javax.net.ssl.trustStorePassword");
+ addCertificatesFromStore(runtimeCerts, file, password);
+
+ // JRE Signer CA keystore
+ file = javaHome + "/lib/security/cacerts";
+ addCertificatesFromStore(runtimeCerts, file, null);
+
+ // JRE Secure Site CA keystore
+ file = (javaHome + "/lib/security/jssecacerts");
+ addCertificatesFromStore(runtimeCerts, file, null);
+
+ // Application-specific keystore for the current user
+ addCertificatesFromStore(runtimeCerts, userTrustStoreLocation, userTrustStorePassword);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void addCertificatesFromStore(CertificateStore store, String file, String password) {
+ try {
+ File f = new File(file);
+ if (f.canRead())
+ store.loadCertificates(f, password);
+ } catch (Exception e) {
+ // Discard errors
+ }
+ }
+
+ @Override /* ITrustStoreProvider */
+ public CertificateStore getRuntimeTrustStore() {
+ return runtimeCerts;
+ }
+
+ @Override /* ITrustStoreProvider */
+ public CertificateStore getSessionTrustStore() {
+ return sessionCerts;
+ }
+
+ @Override /* ITrustStoreProvider */
+ public void installCertificate(Certificate cert) throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
+ File f = new File(userTrustStoreLocation);
+ KeyStore ks = CertificateStore.load(f, userTrustStorePassword);
+ ks.setCertificateEntry(CertificateStore.computeAlias(cert), cert);
+ CertificateStore.store(ks, f, userTrustStorePassword);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.java b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.java
new file mode 100755
index 0000000..a7539fe
--- /dev/null
+++ b/com.ibm.team.juno.client/src/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+
+import javax.net.ssl.*;
+
+/**
+ * A trust manager that will call a registered {@link ICertificateValidator} in
+ * the event that a problematic (e.g. expired, not yet valid) or untrusted
+ * certificate is presented by a server, and react appropriately. This trust
+ * manager will rely on multiple key stores, and manage one of its own. The
+ * managed key store and the session-accepted key store are shared by all trust
+ * manager instances.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public final class ValidatingX509TrustManager implements X509TrustManager {
+
+ // The JRE-provided trust manager used to validate certificates presented by a server.
+ private X509TrustManager baseTrustManager;
+
+ // The registered certificate validator, may be null, called when the base
+ // trust manager rejects a certificate presented by a server.
+ private ICertificateValidator validator;
+
+ private ITrustStoreProvider trustStoreProvider;
+
+ /**
+ * Construct a new ValidatingX509TrustManager.
+ *
+ * @param validator Certificate validator to consult regarding problematic
+ * certificates, or <code>null</code> to always reject them.
+ */
+ public ValidatingX509TrustManager(ICertificateValidator validator) throws KeyStoreException, NoSuchAlgorithmException {
+ this.validator = validator;
+ this.trustStoreProvider = new SharedTrustStoreProvider();
+
+ // Initialize the base X509 trust manager that will be used to evaluate
+ // certificates presented by the server against the runtime trust store.
+ TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init(trustStoreProvider.getRuntimeTrustStore().getKeyStore());
+ TrustManager[] managers = factory.getTrustManagers();
+ for (TrustManager manager : managers) {
+ if (manager instanceof X509TrustManager) {
+ baseTrustManager = (X509TrustManager) manager; // Take the first X509TrustManager we find
+ return;
+ }
+ }
+ throw new IllegalStateException("Couldn't find JRE's X509TrustManager"); //$NON-NLS-1$
+ }
+
+ @Override /* X509TrustManager */
+ public X509Certificate[] getAcceptedIssuers() {
+ return baseTrustManager.getAcceptedIssuers();
+ }
+
+ @Override /* X509TrustManager */
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ baseTrustManager.checkClientTrusted(chain, authType);
+ }
+
+ @Override /* X509TrustManager */
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ X509Certificate cert = chain[0];
+
+ // Has the certificate been OK'd for the session?
+ try {
+ if (trustStoreProvider.getSessionTrustStore().containsCertificate(cert))
+ return;
+ } catch (KeyStoreException e) {
+ // Ignore; proceed to try base trust manager
+ }
+
+ try {
+ // Rely on the base trust manager to check the certificate against the assembled runtime key store
+ baseTrustManager.checkServerTrusted(chain, authType);
+ } catch (CertificateException certEx) {
+
+ // Done if there isn't a validator to consult
+ if (validator == null)
+ throw certEx; // Rejected!
+
+ // Ask the registered certificate validator to rule on the certificate
+ ICertificateValidator.Trust disposition = validator.validate(cert, certEx);
+ switch (disposition) {
+ case REJECT: throw certEx;
+ case ACCEPT_CONNECTION: break;
+ case ACCEPT_SESSION: enterCertificate(cert, false); break;
+ case ACCEPT_PERMANENT: enterCertificate(cert, true); break;
+ }
+ }
+ }
+
+ private void enterCertificate(X509Certificate cert, boolean permanent) throws CertificateException {
+ try {
+ trustStoreProvider.getSessionTrustStore().enterCertificate(cert);
+ if (permanent)
+ trustStoreProvider.installCertificate(cert);
+ } catch (KeyStoreException e) {
+ } catch (NoSuchAlgorithmException e) {
+ } catch (IOException e) {
+ }
+ }
+}