com.blackducksoftware.integration.hub.rest.RestConnection Maven / Gradle / Ivy
/**
* Hub Common Rest
*
* Copyright (C) 2017 Black Duck Software, Inc.
* http://www.blackducksoftware.com/
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.blackducksoftware.integration.hub.rest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.lang3.StringUtils;
import com.blackducksoftware.integration.exception.IntegrationException;
import com.blackducksoftware.integration.hub.rest.exception.IntegrationRestException;
import com.blackducksoftware.integration.log.IntLogger;
import com.blackducksoftware.integration.log.LogLevel;
import com.blackducksoftware.integration.util.proxy.ProxyUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParser;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* The parent class of all Hub connections.
*/
public abstract class RestConnection {
public static final String JSON_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
public final Gson gson = new GsonBuilder().setDateFormat(JSON_DATE_FORMAT).create();
public final JsonParser jsonParser = new JsonParser();
public final OkHttpClient.Builder builder = new OkHttpClient.Builder();
public final Map commonRequestHeaders = new HashMap<>();
public final URL hubBaseUrl;
public int timeout = 120;
public String proxyHost;
public int proxyPort;
public String proxyNoHosts;
public String proxyUsername;
public String proxyPassword;
public IntLogger logger;
private OkHttpClient client;
public static Date parseDateString(final String dateString) throws ParseException {
final SimpleDateFormat sdf = new SimpleDateFormat(JSON_DATE_FORMAT);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.parse(dateString);
}
public static String formatDate(final Date date) {
final SimpleDateFormat sdf = new SimpleDateFormat(JSON_DATE_FORMAT);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(date);
}
public RestConnection(final IntLogger logger, final URL hubBaseUrl, final int timeout) {
this.logger = logger;
this.hubBaseUrl = hubBaseUrl;
this.timeout = timeout;
}
public void connect() throws IntegrationException {
addBuilderConnectionTimes();
addBuilderProxyInformation();
addBuilderAuthentication();
addTlsConnectionInfo();
setClient(builder.build());
clientAuthenticate();
}
public void addTlsConnectionInfo() throws IntegrationException {
final String version = System.getProperty("java.version");
if (hubBaseUrl.getProtocol().equalsIgnoreCase("https") && version.startsWith("1.7") || version.startsWith("1.6")) {
// We do not need to do this for Java 8+
try {
final X509TrustManager trustManager = systemDefaultTrustManager();
// Java 7 does not enable TLS1.2 so we use our TLSSocketFactory to enable all protocols
builder.sslSocketFactory(new TLSSocketFactory(trustManager), trustManager);
} catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new IntegrationException(e);
}
}
}
private X509TrustManager systemDefaultTrustManager() throws IntegrationException {
try {
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
} catch (final GeneralSecurityException e) {
throw new IntegrationException(); // The system has no TLS. Just give up.
}
}
public abstract void addBuilderAuthentication() throws IntegrationException;
public abstract void clientAuthenticate() throws IntegrationException;
private void addBuilderConnectionTimes() {
builder.connectTimeout(timeout, TimeUnit.SECONDS);
builder.writeTimeout(timeout, TimeUnit.SECONDS);
builder.readTimeout(timeout, TimeUnit.SECONDS);
}
private void addBuilderProxyInformation() {
if (shouldUseProxyForUrl(hubBaseUrl)) {
builder.proxy(getProxy(hubBaseUrl));
builder.proxyAuthenticator(new com.blackducksoftware.integration.hub.proxy.OkAuthenticator(proxyUsername, proxyPassword));
}
}
private Proxy getProxy(final URL hubUrl) {
final Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
return proxy;
}
private boolean shouldUseProxyForUrl(final URL url) {
if (StringUtils.isBlank(proxyHost) || proxyPort <= 0) {
return false;
}
final List ignoredProxyHostPatterns = ProxyUtil.getIgnoredProxyHostPatterns(proxyNoHosts);
return !ProxyUtil.shouldIgnoreHost(url.getHost(), ignoredProxyHostPatterns);
}
public HttpUrl createHttpUrl() {
return HttpUrl.get(hubBaseUrl).newBuilder().build();
}
public HttpUrl createHttpUrl(final URL providedUrl) {
final HttpUrl.Builder urlBuilder = HttpUrl.get(providedUrl).newBuilder();
return urlBuilder.build();
}
public HttpUrl createHttpUrl(final String providedUrl) {
final HttpUrl.Builder urlBuilder = HttpUrl.parse(providedUrl).newBuilder();
return urlBuilder.build();
}
public HttpUrl createHttpUrl(final List urlSegments) {
return createHttpUrl(urlSegments, null);
}
public HttpUrl createHttpUrl(final List urlSegments, final Map queryParameters) {
return createHttpUrl(hubBaseUrl.toString(), urlSegments, queryParameters);
}
public HttpUrl createHttpUrl(final String providedUrl, final List urlSegments, final Map queryParameters) {
final HttpUrl.Builder urlBuilder = HttpUrl.parse(providedUrl).newBuilder();
if (urlSegments != null) {
for (final String urlSegment : urlSegments) {
urlBuilder.addPathSegment(urlSegment);
}
}
if (queryParameters != null) {
for (final Entry queryParameter : queryParameters.entrySet()) {
try {
// As of okhttp 3.8.0 the escaped characters are space, ", ', <, >, #, &, and =, so we need to
// encode on our own
// see HttpUrl.java, QUERY_COMPONENT_ENCODE_SET
final String encodedKey = URLEncoder.encode(queryParameter.getKey(), "UTF-8");
final String encodedVal = URLEncoder.encode(queryParameter.getValue(), "UTF-8");
urlBuilder.addEncodedQueryParameter(encodedKey, encodedVal);
} catch (final UnsupportedEncodingException e) {
if (logger != null) {
logger.error(e);
}
}
}
}
return urlBuilder.build();
}
public RequestBody createJsonRequestBody(final String content) {
return createJsonRequestBody("application/json", content);
}
public RequestBody createJsonRequestBody(final String mediaType, final String content) {
return RequestBody.create(MediaType.parse(mediaType), content);
}
public RequestBody createEncodedFormBody(final Map content) {
final FormBody.Builder builder = new FormBody.Builder();
for (final Entry contentEntry : content.entrySet()) {
builder.add(contentEntry.getKey(), contentEntry.getValue());
}
return builder.build();
}
public Request createGetRequest(final HttpUrl httpUrl) {
return createGetRequest(httpUrl, "application/json");
}
public Request createGetRequest(final HttpUrl httpUrl, final String mediaType) {
final Map headers = new HashMap<>();
headers.put("Accept", mediaType);
return createGetRequest(httpUrl, headers);
}
public Request createGetRequest(final HttpUrl httpUrl, final Map headers) {
return getRequestBuilder(headers).url(httpUrl).get().build();
}
public Request createPostRequest(final HttpUrl httpUrl, final RequestBody body) {
return getRequestBuilder().url(httpUrl).post(body).build();
}
public Request createPutRequest(final HttpUrl httpUrl, final RequestBody body) {
return getRequestBuilder().url(httpUrl).put(body).build();
}
public Request createDeleteRequest(final HttpUrl httpUrl) {
return getRequestBuilder().url(httpUrl).delete().build();
}
private Request.Builder getRequestBuilder() {
return getRequestBuilder(null);
}
private Request.Builder getRequestBuilder(final Map headers) {
final Request.Builder builder = new Request.Builder();
final Map requestHeaders = new HashMap<>();
requestHeaders.putAll(commonRequestHeaders);
if (headers != null) {
requestHeaders.putAll(headers);
}
for (final Entry header : requestHeaders.entrySet()) {
builder.addHeader(header.getKey(), header.getValue());
}
return builder;
}
private Request createNewRequest(final Request request) {
final Request.Builder builder = request.newBuilder();
for (final Map.Entry entry : commonRequestHeaders.entrySet()) {
builder.header(entry.getKey(), entry.getValue());
}
return builder.build();
}
public Response handleExecuteClientCall(final Request request) throws IntegrationException {
final long start = System.currentTimeMillis();
logMessage(LogLevel.TRACE, "starting request: " + request.url());
try {
return handleExecuteClientCall(request, 0);
} finally {
final long end = System.currentTimeMillis();
logMessage(LogLevel.TRACE, String.format("completed request: %s (%d ms)", request.url(), end - start));
}
}
private Response handleExecuteClientCall(final Request request, final int retryCount) throws IntegrationException {
if (client != null) {
try {
logRequestHeaders(request);
final Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
try {
if (response.code() == 401 && retryCount < 2) {
connect();
final Request newRequest = createNewRequest(request);
return handleExecuteClientCall(newRequest, retryCount + 1);
} else {
throw new IntegrationRestException(response.code(), response.message(),
String.format("There was a problem trying to %s this item: %s. Error: %s %s", request.method(), request.url().uri().toString(), response.code(), response.message()));
}
} finally {
response.close(); // request was un-sucessful make sure the response is closed to close the body
}
}
logResponseHeaders(response);
return response;
} catch (final IOException e) {
throw new IntegrationException(e.getMessage(), e);
}
} else {
connect();
final Request newRequest = createNewRequest(request);
return handleExecuteClientCall(newRequest, retryCount);
}
}
private void logMessage(final LogLevel level, final String txt) {
if (logger != null) {
if (level == LogLevel.ERROR) {
logger.error(txt);
} else if (level == LogLevel.WARN) {
logger.warn(txt);
} else if (level == LogLevel.INFO) {
logger.info(txt);
} else if (level == LogLevel.DEBUG) {
logger.debug(txt);
} else if (level == LogLevel.TRACE) {
logger.trace(txt);
}
}
}
private boolean isDebugLogging() {
return logger != null && logger.getLogLevel() == LogLevel.TRACE;
}
protected void logRequestHeaders(final Request request) {
if (isDebugLogging()) {
final String requestName = request.getClass().getSimpleName();
logMessage(LogLevel.TRACE, requestName + " : " + request.toString());
logHeaders(requestName, request.headers());
}
}
protected void logResponseHeaders(final Response response) {
if (isDebugLogging()) {
final String responseName = response.getClass().getSimpleName();
logMessage(LogLevel.TRACE, responseName + " : " + response.toString());
logHeaders(responseName, response.headers());
}
}
private void logHeaders(final String requestOrResponseName, final Headers headers) {
if (headers != null && headers.size() > 0) {
logMessage(LogLevel.TRACE, requestOrResponseName + " headers : ");
for (final Entry> headerEntry : headers.toMultimap().entrySet()) {
final String key = headerEntry.getKey();
String value = "null";
if (headerEntry.getValue() != null && !headerEntry.getValue().isEmpty()) {
value = StringUtils.join(headerEntry.getValue(), System.lineSeparator());
}
logMessage(LogLevel.TRACE, String.format("Header %s : %s", key, value));
}
} else {
logMessage(LogLevel.TRACE, requestOrResponseName + " does not have any headers.");
}
}
@Override
public String toString() {
return "RestConnection [baseUrl=" + hubBaseUrl + "]";
}
public OkHttpClient getClient() {
return client;
}
public void setClient(final OkHttpClient client) {
this.client = client;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy