Skip to content

Commit a4184c4

Browse files
authored
Merge pull request #215 from CyberSource/multithreading-fix
Fix for multithreading issues
2 parents d5540af + 6b9153e commit a4184c4

69 files changed

Lines changed: 1119 additions & 1093 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

generator/cybersource-java-template/libraries/okhttp-gson/ApiClient.mustache

Lines changed: 28 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ package Invokers;
1414

1515
import java.io.*;
1616
import java.lang.reflect.Type;
17-
import java.net.HttpRetryException;
1817
import java.net.InetSocketAddress;
1918
import java.net.Proxy;
2019
import java.net.URLConnection;
@@ -29,7 +28,6 @@ import java.text.ParseException;
2928
import java.text.SimpleDateFormat;
3029
import java.util.*;
3130
import java.util.Map.Entry;
32-
import java.util.concurrent.TimeUnit;
3331
import java.util.regex.Matcher;
3432
import java.util.regex.Pattern;
3533
import javax.net.ssl.*;
@@ -43,7 +41,6 @@ import org.apache.logging.log4j.Logger;
4341
import okhttp3.Authenticator;
4442
import okhttp3.Call;
4543
import okhttp3.Callback;
46-
import okhttp3.ConnectionPool;
4744
import okhttp3.Credentials;
4845
import okhttp3.FormBody;
4946
import okhttp3.Headers;
@@ -127,12 +124,11 @@ public class ApiClient {
127124
private KeyManager[] keyManagers;
128125
private String acceptHeader = "";
129126

130-
private static OkHttpClient httpClient;
131-
private final OkHttpClient classHttpClient = initializeFinalVariables();
127+
private OkHttpClient httpClient;
128+
private HttpClientFactoryAdditionalSettings additionalSettings = new HttpClientFactoryAdditionalSettings();
132129

133130
private JSON json;
134131
private String versionInfo;
135-
private static ConnectionPool connectionPool = new ConnectionPool(5, 10, TimeUnit.SECONDS);
136132
private HttpLoggingInterceptor loggingInterceptor;
137133
private long computationStartTime;
138134
private static Logger logger = LogManager.getLogger(ApiClient.class);
@@ -158,44 +154,14 @@ public class ApiClient {
158154
public MerchantConfig merchantConfig;
159155
public RequestTransactionMetrics apiRequestMetrics = new RequestTransactionMetrics();
160156

161-
public static OkHttpClient initializeFinalVariables() {
162-
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
163-
logging.setLevel(Level.NONE);
164-
// connectionPool = new ConnectionPool(5, 10, TimeUnit.SECONDS);
165-
166-
try {
167-
return new OkHttpClient.Builder()
168-
.connectTimeout(1, TimeUnit.SECONDS)
169-
.writeTimeout(60, TimeUnit.SECONDS)
170-
.readTimeout(60, TimeUnit.SECONDS)
171-
.connectionPool(ApiClient.connectionPool)
172-
.addInterceptor(logging)
173-
.build();
174-
}
175-
catch (Exception ex)
176-
{
177-
logger.error("Error in creating HTTP Client");
178-
return null;
179-
}
180-
}
181-
182157
/*
183158
* Constructor for ApiClient
184159
*/
185160
public ApiClient() {
186161
versionInfo = getClientID();
187-
188-
try {
189-
httpClient = classHttpClient.newBuilder()
190-
.retryOnConnectionFailure(true)
191-
.addInterceptor(new RetryInterceptor(this.apiRequestMetrics))
192-
.eventListener(new NetworkEventListener(this.getNewRandomId(), System.nanoTime()))
193-
.build();
194-
}
195-
catch (Exception ex)
196-
{
197-
logger.error("Error in creating HTTP Client");
198-
}
162+
additionalSettings.setCustomRetryOnConnectionFailure(true);
163+
additionalSettings.setCustomRetryInterceptor(new RetryInterceptor(this.apiRequestMetrics));
164+
additionalSettings.setCustomNetworkEventListener(new NetworkEventListener(this.getNewRandomId(), System.nanoTime()));
199165
200166
verifyingSsl = true;
201167
@@ -243,32 +209,13 @@ public class ApiClient {
243209
int proxyPort = merchantConfig.getProxyPort();
244210
String proxyHost = merchantConfig.getProxyAddress();
245211
246-
// User Defined Timeout for HTTP Client
247-
int connectionTimeout = Math.max(merchantConfig.getUserDefinedConnectionTimeout(), 1);
248-
int readTimeout = Math.max(merchantConfig.getUserDefinedReadTimeout(), 60);
249-
int writeTimeout = Math.max(merchantConfig.getUserDefinedWriteTimeout(), 60);
250-
int keepAliveDuration = Math.max(merchantConfig.getUserDefinedKeepAliveDuration(), 10);
251-
connectionPool = new ConnectionPool(5, keepAliveDuration, TimeUnit.SECONDS);
252-
253212
Authenticator proxyAuthenticator;
254213
255214
if (useProxy && (proxyHost != null && !proxyHost.isEmpty())) {
256215
if ((username != null && !username.isEmpty()) && (password != null && !password.isEmpty())) {
257216
proxyAuthenticator = new Authenticator() {
258-
// private int proxyCounter = 0;
259-
260217
@Override
261218
public Request authenticate(Route route, Response response) throws IOException {
262-
// if (proxyCounter++ > 0) {
263-
// if (response.code() == 407) {
264-
// logger.error("HttpRetryException : 407 Proxy Authentication Missing or Incorrect");
265-
// throw new HttpRetryException("Proxy Authentication Missing or Incorrect.", 407);
266-
// } else {
267-
// logger.error("IOException : " + response.message());
268-
// throw new IOException(response.message());
269-
// }
270-
// }
271-
272219
String credential = Credentials.basic(username, password);
273220
return response.request().newBuilder().header("Proxy-Authorization", credential).build();
274221
}
@@ -282,51 +229,15 @@ public class ApiClient {
282229
};
283230
}
284231

285-
try {
286-
httpClient = classHttpClient.newBuilder()
287-
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)))
288-
.proxyAuthenticator(proxyAuthenticator)
289-
.connectTimeout(connectionTimeout, TimeUnit.SECONDS)
290-
.writeTimeout(writeTimeout, TimeUnit.SECONDS)
291-
.readTimeout(readTimeout, TimeUnit.SECONDS)
292-
.retryOnConnectionFailure(true)
293-
.addInterceptor(new RetryInterceptor(this.apiRequestMetrics))
294-
.connectionPool(ApiClient.connectionPool)
295-
.eventListener(new NetworkEventListener(this.getNewRandomId(), System.nanoTime()))
296-
.build();
297-
}
298-
catch (Exception ex)
299-
{
300-
logger.error("Error in creating HTTP Client");
301-
}
302-
303-
this.setHttpClient(httpClient);
232+
additionalSettings.setCustomProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
233+
additionalSettings.setCustomProxyAuthenticator(proxyAuthenticator);
304234
}
305-
else
306-
{
307-
// override the custom timeout in HTTPClient
308-
try {
309-
httpClient = classHttpClient.newBuilder()
310-
.connectTimeout(connectionTimeout, TimeUnit.SECONDS)
311-
.writeTimeout(writeTimeout, TimeUnit.SECONDS)
312-
.readTimeout(readTimeout, TimeUnit.SECONDS)
313-
.connectionPool(ApiClient.connectionPool)
314-
.retryOnConnectionFailure(true)
315-
.addInterceptor(new RetryInterceptor(this.apiRequestMetrics))
316-
.eventListener(new NetworkEventListener(this.getNewRandomId(), System.nanoTime()))
317-
.build();
318-
}
319-
catch (Exception ex)
320-
{
321-
logger.error("Error in creating HTTP Client");
322-
}
323235

324-
this.setHttpClient(httpClient);
325-
}
236+
additionalSettings.setCustomRetryOnConnectionFailure(true);
237+
additionalSettings.setCustomRetryInterceptor(new RetryInterceptor(this.apiRequestMetrics));
238+
additionalSettings.setCustomNetworkEventListener(new NetworkEventListener(this.getNewRandomId(), System.nanoTime()));
326239

327240
this.merchantConfig = merchantConfig;
328-
// RetryInterceptor.retryDelay = merchantConfig.getRetryDelay();
329-
// RetryInterceptor.retryEnabled = merchantConfig.isRetryEnabled();
330241
}
331242

332243
/**
@@ -365,17 +276,6 @@ public class ApiClient {
365276
return httpClient;
366277
}
367278

368-
/**
369-
* Set HTTP client
370-
*
371-
* @param httpClient An instance of OkHttpClient
372-
* @return Api Client
373-
*/
374-
public ApiClient setHttpClient(OkHttpClient httpClient) {
375-
ApiClient.httpClient = httpClient;
376-
return this;
377-
}
378-
379279
/**
380280
* Get JSON
381281
*
@@ -780,9 +680,9 @@ public class ApiClient {
780680
if (debugging) {
781681
loggingInterceptor = new HttpLoggingInterceptor();
782682
loggingInterceptor.setLevel(Level.BODY);
783-
httpClient.interceptors().add(loggingInterceptor);
683+
additionalSettings.setCustomLoggingInterceptor(loggingInterceptor);
784684
} else {
785-
httpClient.interceptors().remove(loggingInterceptor);
685+
additionalSettings.setCustomLoggingInterceptor(null);
786686
loggingInterceptor = null;
787687
}
788688
}
@@ -814,27 +714,6 @@ public class ApiClient {
814714
return this;
815715
}
816716

817-
// /**
818-
// * Get connection timeout (in milliseconds).
819-
// *
820-
// * @return Timeout in milliseconds
821-
// */
822-
// public int getConnectTimeout() {
823-
// return httpClient.connectTimeoutMillis();
824-
// }
825-
826-
// /**
827-
// * Sets the connect timeout (in milliseconds). A value of 0 means no timeout,
828-
// * otherwise values must be between 1 and
829-
// *
830-
// * @param connectionTimeout connection timeout in milliseconds
831-
// * @return Api client
832-
// */
833-
// public ApiClient setConnectTimeout(int connectionTimeout) {
834-
// ApiClient.httpClient = ApiClient.httpClient.newBuilder().connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).build();
835-
// return this;
836-
// }
837-
838717
/**
839718
* @return the computationStartTime
840719
*/
@@ -1071,8 +950,7 @@ public class ApiClient {
1071950
// Expecting string, return the raw response body.
1072951
return (T) respBody;
1073952
} else {
1074-
logger.error(
1075-
"ApiException : Content type \"" + contentType + "\" is not supported for type: " + returnType);
953+
logger.error("ApiException : Content type \"" + contentType + "\" is not supported for type: " + returnType);
1076954
throw new ApiException("Content type \"" + contentType + "\" is not supported for type: " + returnType,
1077955
response.code(), response.headers().toMultimap(), respBody);
1078956
}
@@ -1327,7 +1205,7 @@ public class ApiClient {
13271205
*/
13281206
public Call buildCall(String path, String method, List<Pair> queryParams, Object body,
13291207
Map<String, String> headerParams, Map<String, Object> formParams, String[] authNames,
1330-
ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException {
1208+
ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, ConfigException {
13311209
13321210
//create reqHeader parameter here
13331211
Map<String, String> requestHeaderMap = new HashMap<String, String>();
@@ -1366,6 +1244,11 @@ public class ApiClient {
13661244
logger.info("Request Header Parameters:\n{}", new PrettyPrintingMap<String, String>(headerParams));
13671245
Request request = buildRequest(path, method, queryParams, requestbody, headerParams, formParams, authNames,
13681246
progressRequestListener);
1247+
try {
1248+
httpClient = HttpClientFactory.getHttpClient(this.merchantConfig, this.additionalSettings);
1249+
} catch (ConfigException e) {
1250+
throw e;
1251+
}
13691252
return httpClient.newCall(request);
13701253
}
13711254

@@ -1452,8 +1335,8 @@ public class ApiClient {
14521335
if (versionInfo != null && !versionInfo.isEmpty()) {
14531336
requestHeaderMap.put("v-c-client-id", "cybs-rest-sdk-java-" + versionInfo);
14541337
} else {
1455-
requestHeaderMap.put("v-c-client-id", "cybs-rest-sdk-java-VERSIONUNKNOWN");
1456-
}
1338+
requestHeaderMap.put("v-c-client-id", "cybs-rest-sdk-java-VERSIONUNKNOWN");
1339+
}
14571340

14581341
} catch (ConfigException | IOException e) {
14591342
logger.error(e.getMessage());
@@ -1713,8 +1596,8 @@ public class ApiClient {
17131596
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
17141597
return new java.security.cert.X509Certificate[] {};
17151598
}
1716-
}
1717-
};
1599+
}
1600+
};
17181601
17191602
KeyStore merchantKeyStore = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
17201603
FileInputStream file = new FileInputStream(
@@ -1727,9 +1610,8 @@ public class ApiClient {
17271610
17281611
SSLContext sslContext = SSLContext.getInstance("TLS");
17291612
sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, new SecureRandom());
1730-
httpClient = httpClient.newBuilder()
1731-
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]).build();
1732-
1613+
additionalSettings.setCustomSSLSocketFactory(sslContext.getSocketFactory());
1614+
additionalSettings.setCustomX509TrustManager((X509TrustManager) trustAllCerts[0]);
17331615
} catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException
17341616
| KeyManagementException | UnrecoverableKeyException ex) {
17351617
@@ -1792,11 +1674,11 @@ public class ApiClient {
17921674
if (keyManagers != null || trustManagers != null) {
17931675
SSLContext sslContext = SSLContext.getInstance("TLS");
17941676
sslContext.init(keyManagers, trustManagers, new SecureRandom());
1795-
httpClient = httpClient.newBuilder().sslSocketFactory(sslContext.getSocketFactory()).build();
1677+
additionalSettings.setCustomSSLSocketFactory(sslContext.getSocketFactory());
17961678
} else {
1797-
httpClient = httpClient.newBuilder().sslSocketFactory(null).build();
1679+
additionalSettings.setCustomSSLSocketFactory(null);
17981680
}
1799-
httpClient = httpClient.newBuilder().hostnameVerifier(hostnameVerifier).build();
1681+
additionalSettings.setCustomHostnameVerifier(hostnameVerifier);
18001682
} catch (GeneralSecurityException e) {
18011683
logger.error("RuntimeException : " + e);
18021684
throw new RuntimeException(e);

generator/cybersource-java-template/libraries/okhttp-gson/api.mustache

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {{invokerPackage}}.ProgressResponseBody;
1414
import {{invokerPackage}}.BeanValidationException;
1515
{{/performBeanValidation}}
1616

17+
import com.cybersource.authsdk.core.ConfigException;
1718
import com.google.gson.reflect.TypeToken;
1819

1920
import java.io.IOException;
@@ -80,7 +81,7 @@ public class {{classname}} {
8081
* @return Call to execute
8182
* @throws ApiException If fail to serialize the request body object
8283
*/
83-
public okhttp3.Call {{operationId}}Call({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException {
84+
public okhttp3.Call {{operationId}}Call({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, ConfigException {
8485
SdkTracker sdkTracker = new SdkTracker();
8586
Object localVarPostBody = {{#bodyParam}}sdkTracker.insertDeveloperIdTracker({{paramName}}, {{{dataType}}}.class.getSimpleName(), apiClient.merchantConfig.getRunEnvironment(), apiClient.merchantConfig.getDefaultDeveloperId()){{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};
8687
{{^bodyParam}}
@@ -145,7 +146,7 @@ public class {{classname}} {
145146
}
146147

147148
@SuppressWarnings("rawtypes")
148-
private okhttp3.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException {
149+
private okhttp3.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, ConfigException {
149150
{{^performBeanValidation}}
150151
{{#allParams}}{{#required}}
151152
// verify the required parameter '{{paramName}}' is set
@@ -199,7 +200,7 @@ public class {{classname}} {
199200
* @return {{returnType}}{{/returnType}}
200201
* @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
201202
*/
202-
public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException {
203+
public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException, ConfigException {
203204
logger.info("CALL TO METHOD '{{operationId}}' STARTED");
204205
{{#returnType}}ApiResponse<{{{returnType}}}> {{localVariablePrefix}}resp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
205206
{{#returnType}}
@@ -214,7 +215,7 @@ public class {{classname}} {
214215
* @return ApiResponse&lt;{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Void{{/returnType}}&gt;
215216
* @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
216217
*/
217-
public ApiResponse<{{#vendorExtensions.x-streaming}}InputStream{{/vendorExtensions.x-streaming}}{{^vendorExtensions.x-streaming}}{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}{{/vendorExtensions.x-streaming}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException {
218+
public ApiResponse<{{#vendorExtensions.x-streaming}}InputStream{{/vendorExtensions.x-streaming}}{{^vendorExtensions.x-streaming}}{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}{{/vendorExtensions.x-streaming}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException, ConfigException {
218219
this.apiClient.setComputationStartTime(System.nanoTime());
219220
okhttp3.Call {{localVariablePrefix}}call = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}null, null);
220221
{{#returnType}}Type {{localVariablePrefix}}localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType();
@@ -229,7 +230,7 @@ public class {{classname}} {
229230
* @return The request call
230231
* @throws ApiException If fail to process the API call, e.g. serializing the request body object
231232
*/
232-
public okhttp3.Call {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException {
233+
public okhttp3.Call {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException, ConfigException {
233234
234235
this.apiClient.setComputationStartTime(System.nanoTime());
235236
ProgressResponseBody.ProgressListener progressListener = null;

generator/cybersource_java_sdk_gen.bat

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ git checkout ..\src\main\java\Api\OAuthApi.java
2525
git checkout ..\src\main\java\Api\BatchUploadwithMTLSApi.java
2626
git checkout ..\src\main\java\Model\AccessTokenResponse.java
2727
git checkout ..\src\main\java\Model\CreateAccessTokenRequest.java
28+
git checkout ..\src\main\java\Invokers\HttpClientFactory.java
29+
git checkout ..\src\main\java\Invokers\HttpClientFactoryAdditionalSettings.java
2830

2931
git checkout ..\pom.xml
3032
git checkout ..\README.md

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@
304304
<dependency>
305305
<groupId>com.cybersource</groupId>
306306
<artifactId>AuthenticationSdk</artifactId>
307-
<version>0.0.38</version>
307+
<version>0.0.39-SNAPSHOT</version>
308308
</dependency>
309309

310310
<dependency>

0 commit comments

Comments
 (0)