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

com.openshift.restclient.ClientBuilder Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2016 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/

package com.openshift.restclient;

import java.io.IOException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;

import com.openshift.internal.restclient.DefaultClient;
import com.openshift.internal.restclient.ResourceFactory;
import com.openshift.internal.restclient.authorization.AuthorizationContext;
import com.openshift.internal.restclient.okhttp.AuthenticatorInterceptor;
import com.openshift.internal.restclient.okhttp.ResponseCodeInterceptor;
import com.openshift.restclient.http.IHttpConstants;
import com.openshift.restclient.utils.SSLUtils;

import okhttp3.Authenticator;
import okhttp3.Dispatcher;
import okhttp3.Interceptor;
import okhttp3.OkHttp;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Builder to create IClient instances.
 *
 */
public class ClientBuilder {

    private String baseUrl;
    private ISSLCertificateCallback sslCertificateCallback = new NoopSSLCertificateCallback();
    private boolean sslCertCallbackWithDefaultHostnameVerifier = false;
    private X509Certificate certificate;
    private Collection certificateCollection;
    private String certificateAlias;
    private IResourceFactory resourceFactory;
    private String userName;
    private String token;
    private String password;
    private String userAgentPrefix;

    private Proxy proxy;
    private ProxySelector proxySelector;
    private Authenticator proxyAuthenticator;

    private int maxRequests = 64;
    private int maxRequestsPerHost = 10;

    private int readTimeout = IHttpConstants.DEFAULT_READ_TIMEOUT;
    private TimeUnit readTimeoutUnit = TimeUnit.MILLISECONDS;
    private int connectTimeout = IHttpConstants.DEFAULT_READ_TIMEOUT;
    private TimeUnit connectTimeoutUnit = TimeUnit.MILLISECONDS;
    private int writeTimeout = IHttpConstants.DEFAULT_READ_TIMEOUT;
    private TimeUnit writeTimeoutUnit = TimeUnit.MILLISECONDS;
    private int pingInterval = 0;
    private TimeUnit pingIntervalUnit = TimeUnit.MILLISECONDS;

    public ClientBuilder() {
        this(null);
    }

    public ClientBuilder(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    public ClientBuilder sslCertificateCallback(ISSLCertificateCallback callback) {
        this.sslCertificateCallback = callback == null ? new NoopSSLCertificateCallback() : callback;
        return this;
    }

    public ClientBuilder sslCertCallbackWithDefaultHostnameVerifier(boolean b) {
        this.sslCertCallbackWithDefaultHostnameVerifier = b;
        return this;
    }

    public ClientBuilder sslCertificate(String alias, X509Certificate cert) {
        this.certificateAlias = alias;
        this.certificate = cert;
        return this;
    }

    public ClientBuilder sslCertificateCollection(String alias, Collection certs) {
        this.certificateAlias = alias;
        this.certificateCollection = certs;
        return this;
    }

    public ClientBuilder resourceFactory(IResourceFactory factory) {
        this.resourceFactory = factory;
        return this;
    }

    public ClientBuilder toCluster(String baseUrl) {
        this.baseUrl = baseUrl;
        return this;
    }

    public ClientBuilder withUserName(String userName) {
        this.userName = userName;
        return this;
    }

    public ClientBuilder withPassword(String password) {
        this.password = password;
        return this;
    }

    public ClientBuilder usingToken(String token) {
        this.token = token;
        return this;
    }

    public ClientBuilder usingUserAgentPrefix(String prefix) {
        this.userAgentPrefix = prefix;
        return this;
    }

    public ClientBuilder withConnectTimeout(int timeout, TimeUnit unit) {
        this.connectTimeout = timeout;
        this.connectTimeoutUnit = unit;
        return this;
    }

    /**
     * The connect timeout parameter used for establishing the connection to a
     * remote server
     *
     * @param connectInMillis
     *            A value in milliseconds
     */
    public ClientBuilder withConnectTimeout(int connectInMillis) {
        this.connectTimeout = connectInMillis;
        return this;
    }

    public ClientBuilder withReadTimeout(int timeout, TimeUnit unit) {
        this.readTimeout = timeout;
        this.readTimeoutUnit = unit;
        return this;
    }

    public ClientBuilder withWriteTimeout(int timeout, TimeUnit unit) {
        this.writeTimeout = timeout;
        this.writeTimeoutUnit = unit;
        return this;
    }

    public ClientBuilder withPingInterval(int pingInterval, TimeUnit unit) {
        this.pingInterval = pingInterval;
        this.pingIntervalUnit = unit;
        return this;
    }

    public ClientBuilder proxy(Proxy proxy) {
        this.proxy = proxy;
        return this;
    }

    public ClientBuilder proxySelector(ProxySelector proxySelector) {
        this.proxySelector = proxySelector;
        return this;
    }


    public ClientBuilder proxyAuthenticator(Authenticator proxyAuthenticator) {
        this.proxyAuthenticator = proxyAuthenticator;
        return this;
    }


    /**
     * The maximum concurrent requests for this client.
     *
     * @param maxRequests
     *            the maximum number of concurrent requests
     * @return the client builder
     */
    public ClientBuilder withMaxRequests(int maxRequests) {
        this.maxRequests = maxRequests;
        return this;
    }

    /**
     * The maximum concurrent request for this client for a single host.
     *
     * @param maxRequestsPerHost
     *            the maximum number of concurrent requests for a single host
     * @return the client builder
     */
    public ClientBuilder withMaxRequestsPerHost(int maxRequestsPerHost) {
        this.maxRequestsPerHost = maxRequestsPerHost;
        return this;
    }

    /**
     * Build a client
     *
     * @throws KeyManagementException an exception
     */
    public IClient build() {
        try {
            TrustManagerFactory trustManagerFactory = initTrustManagerFactory(certificateAlias, certificate,
                    certificateCollection);
            X509TrustManager trustManager = getCurrentTrustManager(trustManagerFactory);
            SSLContext sslContext = SSLUtils.getSSLContext(trustManager);

            AuthenticatorInterceptor authenticatorInterceptor = new AuthenticatorInterceptor();
            ResponseCodeInterceptor responseCodeInterceptor = new ResponseCodeInterceptor();
            Dispatcher dispatcher = createDispatcher();

            OkHttpClient okClient = 
                    createOkHttpClient(trustManager, sslContext, authenticatorInterceptor, responseCodeInterceptor, dispatcher);

            IResourceFactory factory = (IResourceFactory) ObjectUtils.defaultIfNull(resourceFactory, new ResourceFactory(null));
            AuthorizationContext authContext = new AuthorizationContext(token, userName, password);
            DefaultClient client = new DefaultClient(new URL(this.baseUrl), okClient, factory, null, authContext);

            authContext.setClient(client);
            authenticatorInterceptor.setClient(client);
            responseCodeInterceptor.setClient(client);
            factory.setClient(client);
            return client;
        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
                | IOException e) {
            throw new OpenShiftException(e, "Unable to initialize client");
        }
    }

    private Dispatcher createDispatcher() {
        Dispatcher dispatcher = new Dispatcher();

        // hiding these for now to since not certain
        // if we need to really expose them.
        dispatcher.setMaxRequests(maxRequests);
        dispatcher.setMaxRequestsPerHost(maxRequestsPerHost);
        return dispatcher;
    }

    private OkHttpClient createOkHttpClient(X509TrustManager trustManager, SSLContext sslContext,
            AuthenticatorInterceptor authenticatorInterceptor, ResponseCodeInterceptor responseCodeInterceptor, Dispatcher dispatcher) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .addNetworkInterceptor(new UserAgentInterceptor(userAgentPrefix))
                .addInterceptor(responseCodeInterceptor)
                .addInterceptor(authenticatorInterceptor)
                .dispatcher(dispatcher)
                .readTimeout(readTimeout, readTimeoutUnit)
                .writeTimeout(writeTimeout, writeTimeoutUnit)
                .connectTimeout(connectTimeout, connectTimeoutUnit)
                .pingInterval(pingInterval, pingIntervalUnit)
                .sslSocketFactory(sslContext.getSocketFactory(), trustManager);

        if (!this.sslCertCallbackWithDefaultHostnameVerifier) {
            builder.hostnameVerifier(sslCertificateCallback);
        }

        if (proxy != null) {
            builder.proxy(proxy);
        }

        if (proxySelector != null) {
            builder.proxySelector(proxySelector);
        }

        if (proxyAuthenticator != null) {
            builder.proxyAuthenticator(proxyAuthenticator);
        }

        return builder.build();
    }

    private X509TrustManager getCurrentTrustManager(TrustManagerFactory trustManagerFactory)
            throws NoSuchAlgorithmException, KeyStoreException {
        for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
            if (trustManager instanceof X509TrustManager) {
                X509TrustManager x509TrustManager = (X509TrustManager) trustManager;
                return new CallbackTrustManager(x509TrustManager, this.sslCertificateCallback);
            }
        }
        return null;

    }

    private TrustManagerFactory initTrustManagerFactory(String alias, X509Certificate cert,
            Collection certs)
            throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        if (alias != null && (cert != null || certs != null)) {
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            // need this load to initialize the key store, and allow for the subsequent set
            // certificate entry
            ks.load(null, null);
            if (cert != null) {
                cert.checkValidity();
                ks.setCertificateEntry(alias, cert);
            }
            if (certs != null) {
                int i = 0;
                for (X509Certificate x509 : certs) {
                    x509.checkValidity();
                    ks.setCertificateEntry(alias + i, x509);
                    i++;
                }
            }

            // testing has proven that you can only call init() once for a
            // TrustManagerFactory wrt loading certs
            // from the KeyStore ... subsequent KeyStore.setCertificateEntry /
            // TrustManagerFactory.init calls are
            // ignored.
            // So if a specific cert is required to validate this connection's communication
            // with the server, add it up front
            // in the ctor.
            trustManagerFactory.init(ks);
        } else {
            trustManagerFactory.init((KeyStore) null);
        }
        return trustManagerFactory;
    }

    private static class CallbackTrustManager implements X509TrustManager {

        private X509TrustManager trustManager;
        private ISSLCertificateCallback callback;

        private CallbackTrustManager(X509TrustManager currentTrustManager, ISSLCertificateCallback callback)
                throws NoSuchAlgorithmException, KeyStoreException {
            this.trustManager = currentTrustManager;
            this.callback = callback;
        }

        public X509Certificate[] getAcceptedIssuers() {
            return trustManager.getAcceptedIssuers();
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            try {
                trustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException e) {
                if (!callback.allowCertificate(chain)) {
                    throw e;
                }
            }
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            trustManager.checkServerTrusted(chain, authType);
        }
    }

    private static class UserAgentInterceptor implements Interceptor {
        
        private final String userAgent;
        
        public UserAgentInterceptor(String userAgentPrefix) {
            this.userAgent = StringUtils.join(
                    new String[]{ userAgentPrefix, "openshift-restclient-java", "okhttp/" + OkHttp.VERSION }, "/");
        }


        @Override
        public Response intercept(Chain chain) throws IOException {
            Request agent = chain.request().newBuilder().header("User-Agent", userAgent).build();
            return chain.proceed(agent);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy