/*************************************************************************** * Copyright (c) 2014 VMware, Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ package com.vmware.bdd.plugin.clouderamgr.service; import com.cloudera.api.ApiObjectMapper; import com.cloudera.api.ApiRootResource; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; import com.google.common.annotations.VisibleForTesting; import com.vmware.bdd.utils.DefaultTrustManager; import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.feature.AbstractFeature; import org.apache.cxf.feature.LoggingFeature; import org.apache.cxf.jaxrs.client.ClientConfiguration; import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.concurrent.TimeUnit; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; public class ClouderaManagerClientBuilder { public static final int DEFAULT_TCP_PORT = 7180; public static final long DEFAULT_CONNECTION_TIMEOUT = 0; public static final TimeUnit DEFAULT_CONNECTION_TIMEOUT_UNITS = TimeUnit.MILLISECONDS; public static final long DEFAULT_RECEIVE_TIMEOUT = 0; public static final TimeUnit DEFAULT_RECEIVE_TIMEOUT_UNITS = TimeUnit.MILLISECONDS; private URL baseUrl; private String hostname; private int port = DEFAULT_TCP_PORT; private boolean enableTLS = false; private boolean enableLogging = false; private String username; private String password; private long connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; private TimeUnit connectionTimeoutUnits = DEFAULT_CONNECTION_TIMEOUT_UNITS; private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT; private TimeUnit receiveTimeoutUnits = DEFAULT_RECEIVE_TIMEOUT_UNITS; private boolean validateCerts = true; private boolean validateCn = true; public ClouderaManagerClientBuilder withBaseURL(URL baseUrl) { this.baseUrl = baseUrl; return this; } public ClouderaManagerClientBuilder withHost(String hostname) { this.hostname = hostname; return this; } public ClouderaManagerClientBuilder withPort(int port) { this.port = port; return this; } public ClouderaManagerClientBuilder enableTLS() { this.enableTLS = true; return this; } public ClouderaManagerClientBuilder enableLogging() { this.enableLogging = true; return this; } public ClouderaManagerClientBuilder withUsernamePassword(String username, String password) { this.username = username; this.password = password; return this; } public ClouderaManagerClientBuilder withConnectionTimeout( long connectionTimeout, TimeUnit connectionTimeoutUnits) { this.connectionTimeout = connectionTimeout; this.connectionTimeoutUnits = connectionTimeoutUnits; return this; } public ClouderaManagerClientBuilder withReceiveTimeout(long receiveTimeout, TimeUnit receiveTimeoutUnits) { this.receiveTimeout = receiveTimeout; this.receiveTimeoutUnits = receiveTimeoutUnits; return this; } public ClouderaManagerClientBuilder disableTlsCertValidation() { this.validateCerts = false; return this; } public ClouderaManagerClientBuilder disableTlsCnValidation() { this.validateCn = false; return this; } @VisibleForTesting String generateAddress() { final String apiRootPath = "api/"; if (baseUrl != null) { // Short-circuit and use the base URL to generate the full URL return String.format("%s/%s", baseUrl.toExternalForm(), apiRootPath); } if (hostname == null) { throw new IllegalArgumentException("hostname or full url must be set"); } if (port <= 0) { throw new IllegalArgumentException(String.format( "'%d' is not a valid port number", port)); } String urlString = String.format("%s://%s:%d/%s", enableTLS ? "https" : "http", hostname, port, apiRootPath); try { // Check the syntax of the generated URL string new URI(urlString); } catch (URISyntaxException e) { throw new IllegalArgumentException(String.format( "'%s' is not a valid hostname", hostname), e); } return urlString; } public ApiRootResource build() { return build(ApiRootResource.class); } protected <T> T build(Class<T> proxyType) { JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); String address = generateAddress(); boolean isTlsEnabled = address.startsWith("https://"); bean.setAddress(address); if (username != null) { bean.setUsername(username); bean.setPassword(password); } if (enableLogging) { bean.setFeatures(Arrays.<AbstractFeature> asList(new LoggingFeature())); } bean.setResourceClass(proxyType); bean.setProvider(new JacksonJsonProvider(new ApiObjectMapper())); T rootResource = bean.create(proxyType); ClientConfiguration config = WebClient.getConfig(rootResource); HTTPConduit conduit = (HTTPConduit) config.getConduit(); if (isTlsEnabled) { TLSClientParameters tlsParams = new TLSClientParameters(); if (!validateCerts) { tlsParams .setTrustManagers(new TrustManager[] { new AcceptAllTrustManager() }); } else { tlsParams .setTrustManagers(new TrustManager[] { new DefaultTrustManager() }); } tlsParams.setDisableCNCheck(!validateCn); conduit.setTlsClientParameters(tlsParams); } HTTPClientPolicy policy = conduit.getClient(); policy.setConnectionTimeout(connectionTimeoutUnits .toMillis(connectionTimeout)); policy.setReceiveTimeout(receiveTimeoutUnits.toMillis(receiveTimeout)); return rootResource; } /** * Closes the transport level conduit in the client. Reopening a new * connection, requires creating a new client object using the build() method * in this builder. * * @param root * The resource returned by the build() method of this builder * class */ public static void closeClient(ApiRootResource root) { ClientConfiguration config = WebClient.getConfig(root); HTTPConduit conduit = config.getHttpConduit(); if (conduit == null) { throw new IllegalArgumentException( "Client is not using the HTTP transport"); } conduit.close(); } /** A trust manager that will accept all certificates. */ private static class AcceptAllTrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) { // no op. } public void checkServerTrusted(X509Certificate[] chain, String authType) { // no op. } public X509Certificate[] getAcceptedIssuers() { return null; } } }