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 ([email protected]) + */ +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 ([email protected]) + */ +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 ([email protected]) + */ +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 ([email protected]) + */ +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 ([email protected]) + */ +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) { + } + } +}
