Skip to content

Commit df9ff9d

Browse files
committed
putting mti filed check in case of connection pooling
1 parent 7664673 commit df9ff9d

5 files changed

Lines changed: 91 additions & 116 deletions

File tree

java/src/main/java/com/cybersource/ws/client/Client.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ public class Client {
5151

5252
private static final String ELEM_NVP_REPLY = "nvpReply";
5353

54-
private static final String MERCHANT_ID = "merchantID";
55-
private static final String KEY_ALIAS = "keyAlias";
56-
5754
private static ConcurrentHashMap<String, MerchantConfig> mcObjects = new ConcurrentHashMap<String, MerchantConfig>();
5855

5956
/**
@@ -103,10 +100,15 @@ public static Map<String, String> runTransaction(
103100
}
104101

105102
setVersionInformation(request);
106-
if(mc.retryIfMTIFieldExistEnabled()) {
107-
setMTIFieldIfNotExist(request);
103+
104+
if (mc.getUseHttpClientWithConnectionPool()){
105+
String mtiField = request.get(MERCHANT_TRANSACTION_IDENTIFIER);
106+
if(StringUtils.isBlank(mtiField)) {
107+
throw new ClientException(HTTP_BAD_REQUEST, MTI_FIELD_ERR_MSG, false, logger);
108+
}
108109
}
109110

111+
110112
logger = new LoggerWrapper(_logger, prepare, logTranStart, mc);
111113

112114
DocumentBuilder builder = Utility.newDocumentBuilder();
@@ -184,6 +186,7 @@ public static Map<String, String> runTransaction(
184186
* Sets the version information in the request.
185187
*
186188
* @param request request to set the version information in.
189+
*
187190
*/
188191
private static void setVersionInformation(Map<String, String> request) {
189192
request.put(ELEM_CLIENT_LIBRARY, Utility.NVP_LIBRARY);
@@ -234,7 +237,7 @@ private static Document soapWrapAndSign(
234237

235238
if ( mc.getUseSignAndEncrypted() ) {
236239
// Encrypt signed Document
237-
resultDocument = SecurityUtil.handleMessageCreation(resultDocument, request.get(MERCHANT_ID), logger);
240+
resultDocument = SecurityUtil.handleMessageCreation(resultDocument, request.get(ELEM_MERCHANT_ID), logger);
238241
logger.log(Logger.LT_INFO, "Client, End of handleMessageCreation ", true);
239242
}
240243
if (logSignedData) {
@@ -325,13 +328,13 @@ static String mapToString(Map src, boolean mask, int type) {
325328
*/
326329
static private MerchantConfig getMerchantConfigObject(Map<String, String> request, Properties props) throws ConfigException {
327330
MerchantConfig mc;
328-
String merchantID = request.get(MERCHANT_ID);
331+
String merchantID = request.get(ELEM_MERCHANT_ID);
329332
if (merchantID == null) {
330333
// if no merchantID is present in the request, get its
331334
// value from the properties and add it to the request.
332335
mc = new MerchantConfig(props, null);
333336
merchantID = mc.getMerchantID();
334-
request.put(MERCHANT_ID, merchantID);
337+
request.put(ELEM_MERCHANT_ID, merchantID);
335338
} else {
336339
mc = new MerchantConfig(props, merchantID);
337340
}
@@ -346,11 +349,11 @@ static private MerchantConfig getMerchantConfigObject(Map<String, String> reques
346349
* @return String
347350
*/
348351
private static String getMerchantId(Map<String, String> request, Properties props) {
349-
String merchantID = request.get(MERCHANT_ID);
352+
String merchantID = request.get(ELEM_MERCHANT_ID);
350353
if (merchantID == null) {
351354
// if no merchantID is present in the request, get its
352355
// value from the properties
353-
merchantID = props.getProperty(MERCHANT_ID);
356+
merchantID = props.getProperty(ELEM_MERCHANT_ID);
354357
}
355358
return merchantID;
356359
}
@@ -389,12 +392,12 @@ private static MerchantConfig getInstanceMap(Map<String, String> request, Proper
389392
}
390393
}
391394
MerchantConfig mc = mcObjects.get(midOrKeyAlias);
392-
String merchantID = request.get(MERCHANT_ID);
395+
String merchantID = request.get(ELEM_MERCHANT_ID);
393396
// if no merchantID is present in the request, get its
394397
// value from the properties and add it to the request.
395398
if(StringUtils.isEmpty(merchantID)) {
396399
merchantID = mc.getMerchantID();
397-
request.put(MERCHANT_ID, merchantID);
400+
request.put(ELEM_MERCHANT_ID, merchantID);
398401
}
399402
return mc;
400403
}

java/src/main/java/com/cybersource/ws/client/MerchantConfig.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,11 @@ public int getLogMaximumSize() {
253253
}
254254

255255
/**
256-
* retryIfMTIFieldExist If enabled then SDK will include the merchantTransactionIdentifier field in the original
257-
* request if not passed already in payload for authorization, capture, sale, follow-on credit, or stand-alone credit, etc).
258-
* The value of the merchantTransactionIdentifier field must be unique
256+
* retryIfMTIFieldExist If enabled and merchantTransactionIdentifier field is passed in payload then SDK will
257+
* retry the transaction in case when connection pooling http client is used and if SDK receives an I/O error/exception, when executing
258+
* a request over a connection that has been closed at the server side.
259+
*
260+
* The value of the merchantTransactionIdentifier[MTI] field must be unique
259261
*
260262
* If not enabled, a transaction may fail(retry wont occur in some cases) if while sending transaction SDK receives an I/O error/exception, when executing
261263
* a request over a connection that has been closed at the server side.
@@ -666,7 +668,7 @@ public MerchantConfig(Properties _props, String _merchantID)
666668
validateAfterInactivityMs = getIntegerProperty(merchantID, "validateAfterInactivityMs", 0);
667669
staleConnectionCheckEnabled = getBooleanProperty(merchantID, "staleConnectionCheckEnabled", true);
668670
shutdownHookEnabled = getBooleanProperty(merchantID, "enabledShutdownHook", true);
669-
retryIfMTIFieldExist = getBooleanProperty(merchantID, "retryIfMTIFieldExist", false);
671+
retryIfMTIFieldExist = getBooleanProperty(merchantID, "retryIfMTIFieldExist", true);
670672
}
671673

672674
allowRetry = getBooleanProperty(merchantID, "allowRetry", true);

java/src/main/java/com/cybersource/ws/client/Utility.java

Lines changed: 21 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
3030
import javax.xml.transform.dom.DOMSource;
3131
import javax.xml.transform.stream.StreamResult;
3232
import java.io.*;
33-
import java.math.BigDecimal;
34-
import java.math.BigInteger;
3533
import java.net.InetAddress;
36-
import java.net.NetworkInterface;
3734
import java.net.UnknownHostException;
38-
import java.util.*;
35+
import java.util.HashMap;
36+
import java.util.Iterator;
37+
import java.util.Map;
38+
import java.util.Properties;
3939

4040
/**
4141
* Class containing useful constants and methods.
@@ -58,20 +58,11 @@ private Utility() {
5858
public static final String ELEM_CLIENT_LIBRARY = "clientLibrary";
5959
public static final String ELEM_CLIENT_LIBRARY_VERSION = "clientLibraryVersion";
6060
public static final String ELEM_CLIENT_ENVIRONMENT = "clientEnvironment";
61-
private static long lastTick = System.currentTimeMillis();
62-
private static final Object lastTickLock = new Object();
63-
private static InetAddress addr;
64-
65-
static {
66-
try {
67-
addr = InetAddress.getLocalHost();
68-
validateIPAddress(addr);
69-
} catch (UnknownHostException e) {
70-
e.printStackTrace();
71-
}
72-
73-
}
74-
61+
/**
62+
* HTTP Status-Code 400: Bad Request.
63+
*/
64+
public static final int HTTP_BAD_REQUEST = 400;
65+
public static final String MTI_FIELD_ERR_MSG = "merchantTransactionIdentifier field is mandatory if useHttpClientWithConnectionPool is set to true";
7566

7667
/**
7768
* If in the Request map, a key called "_has_escapes" is present and is set
@@ -580,10 +571,7 @@ public static long getResponseTransitTime(long resIssuedAtTime) {
580571
}
581572

582573
private static boolean checkIfEpochTimeInSecs(long resIssuedAtTime) {
583-
if(String.valueOf(resIssuedAtTime).length() == 10) {
584-
return true;
585-
}
586-
return false;
574+
return String.valueOf(resIssuedAtTime).length() == 10;
587575
}
588576

589577
private static long parseLong(String val, long defaultValue) {
@@ -598,77 +586,20 @@ private static long parseLong(String val, long defaultValue) {
598586
return result;
599587
}
600588

601-
/**
602-
* Use this to pre-set a merchantTransactionIdentifier before sending the request.
603-
* This is a unique value for each ICSRequest. The format for the
604-
* request id is as follows:
605-
* 0916351920802167904518
606-
* Where the first 12 digits of the of the number is the
607-
* number of milliseconds since the epoch (Jan 1, 1970, 00:00 UTC).
608-
* The next 10 digits is the ip address of the hostname, represented
609-
* as a 32 bit integer in decimal format.
610-
*/
611-
public static void setMTIFieldIfNotExist(Map<String, String> request) {
612-
String mti = request.get(MERCHANT_TRANSACTION_IDENTIFIER);
613-
if (StringUtils.isBlank(mti)) {
614-
request.put(MERCHANT_TRANSACTION_IDENTIFIER, generateMTI());
615-
}
616-
}
617-
618-
public static String generateMTI() {
619-
if(addr == null){
620-
return null;
589+
public static String checkIfMTIFiledExist(Document request, String nsURI) {
590+
Element element = Utility.getElement(request, MERCHANT_TRANSACTION_IDENTIFIER, nsURI);
591+
String mtiFieldValue = checkIfMTIFiledExist(element);
592+
if(StringUtils.isBlank(mtiFieldValue)){
593+
element = Utility.getElement(request, MERCHANT_TRANSACTION_IDENTIFIER, null);
594+
mtiFieldValue = checkIfMTIFiledExist(element);
621595
}
622-
BigInteger ip = new BigInteger(1, addr.getAddress());
623-
624-
// pad the ip address string to 10 characters
625-
String ipString = ip.toString();
626-
if (ipString.length() < 10) {
627-
ipString = "0000000000".substring(ipString.length()) + ipString;
628-
}
629-
630-
// trim leading characters in case it's > 10 digits long
631-
if (ipString.length() > 10) {
632-
ipString = ipString.substring(ipString.length() - 10);
633-
}
634-
635-
// pad the time string to 12 characters
636-
String timeString;
637-
timeString = String.valueOf(newTick());
638-
if (timeString.length() < 12) {
639-
timeString = "000000000000".substring(timeString.length()) + timeString;
640-
}
641-
642-
// tim leading characters if time > 12 digits.
643-
if (timeString.length() > 12) {
644-
timeString = timeString.substring(timeString.length() - 12);
645-
}
646-
return (timeString + ipString);
596+
return mtiFieldValue;
647597
}
648598

649-
private static long newTick() {
650-
return newTick(System.currentTimeMillis());
651-
}
652-
653-
private static long newTick(long currentTimeMillis) {
654-
synchronized (lastTickLock) {
655-
if (currentTimeMillis <= lastTick) {
656-
currentTimeMillis = lastTick + 1;
657-
}
658-
lastTick = currentTimeMillis;
659-
}
660-
return lastTick;
661-
}
662-
663-
/**
664-
* Validates the IP address. The only validation currenly
665-
* being made is the check against 127.0.0.1.
666-
**/
667-
private static void validateIPAddress(InetAddress addr)
668-
throws UnknownHostException {
669-
if (addr.equals(InetAddress.getByName("127.0.0.1"))) {
670-
throw new UnknownHostException(
671-
"127.0.0.1 is not allowed. Use a different IP address.");
599+
public static String checkIfMTIFiledExist(Element element) {
600+
if(element != null){
601+
return element.getAttribute(MERCHANT_TRANSACTION_IDENTIFIER);
672602
}
603+
return null;
673604
}
674605
}

java/src/main/java/com/cybersource/ws/client/XMLClient.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,13 @@ public static Document runTransaction(
169169

170170
nsURI = mc.getEffectiveNamespaceURI();
171171

172+
if (mc.getUseHttpClientWithConnectionPool()){
173+
String mtiField = checkIfMTIFiledExist(request, nsURI);
174+
if(StringUtils.isBlank(mtiField)) {
175+
throw new ClientException(HTTP_BAD_REQUEST, MTI_FIELD_ERR_MSG, false, logger);
176+
}
177+
}
178+
172179
logger = new LoggerWrapper(_logger, prepare, logTranStart, mc);
173180

174181
setVersionInformation(request, nsURI, mc.retryIfMTIFieldExistEnabled());
@@ -235,7 +242,6 @@ public static Document runTransaction(
235242
}
236243
}
237244

238-
239245
/**
240246
* Sets the merchantID in the request.
241247
*
@@ -329,12 +335,6 @@ private static void setVersionInformation(Document request, String nsURI, boolea
329335
versionsFragment.appendChild(Utility.createElement(
330336
request, nsURI, ELEM_CLIENT_ENVIRONMENT, Utility.ENVIRONMENT));
331337

332-
if(isAddingMTIEnabled && Utility.getElement(
333-
request, MERCHANT_TRANSACTION_IDENTIFIER, nsURI)==null) {
334-
versionsFragment.appendChild(Utility.createElement(
335-
request, nsURI, MERCHANT_TRANSACTION_IDENTIFIER, generateMTI()));
336-
}
337-
338338
// if the current element is not null, it will be the sibling right
339339
// next to the version fields.
340340
if (currElem != null) {
@@ -593,8 +593,6 @@ private static MerchantConfig getInstanceMap(Document request, Properties props)
593593
}
594594
return mc;
595595
}
596-
597-
598596
}
599597

600598

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,60 @@
11
package com.cybersource.ws.client;
22

3+
import org.junit.Assert;
4+
import org.junit.Before;
5+
import org.junit.BeforeClass;
36
import org.junit.Test;
47

8+
import java.io.IOException;
9+
import java.io.InputStream;
510
import java.lang.reflect.InvocationTargetException;
611
import java.util.Map;
12+
import java.util.Properties;
713

14+
import static com.cybersource.ws.client.Utility.*;
815
import static org.junit.Assert.assertEquals;
916

1017
public class ClientTest extends BaseTest {
18+
19+
Map<String, String> request = getSampleRequest();
20+
1121
@Test
1222
public void testSetVersionInformation() throws InvocationTargetException {
1323
Class[] argClasses = {Map.class};
14-
Map<String,String> request = getSampleRequest();
1524
Object[] argObjects = {request};
1625
invokePrivateStaticMethod(Client.class, "setVersionInformation", argClasses, argObjects);
1726
assertEquals(Utility.NVP_LIBRARY, request.get("clientLibrary"));
1827
}
28+
29+
@Test
30+
public void testShouldFailedIfMTIFieldNotExist_1() throws FaultException {
31+
merchantProperties.put("useHttpClientWithConnectionPool", "true");
32+
merchantProperties.put("retryIfMTIFieldExist", "true");
33+
try {
34+
Client.runTransaction(request, merchantProperties);
35+
Assert.fail();
36+
} catch (ClientException e) {
37+
Assert.assertEquals(MTI_FIELD_ERR_MSG, e.getHttpError());
38+
Assert.assertFalse(e.isCritical());
39+
Assert.assertNull(e.getInnerException());
40+
Assert.assertEquals("ClientException: (" + HTTP_BAD_REQUEST + ") " + MTI_FIELD_ERR_MSG, e.getLocalizedMessage());
41+
Assert.assertEquals(400, e.getHttpStatusCode());
42+
}
43+
}
44+
45+
@Test
46+
public void testShouldFailedIfMTIFieldNotExist_2() throws FaultException {
47+
merchantProperties.put("useHttpClientWithConnectionPool", "true");
48+
merchantProperties.put("retryIfMTIFieldExist", "false");
49+
try {
50+
Client.runTransaction(request, merchantProperties);
51+
Assert.fail();
52+
} catch (ClientException e) {
53+
Assert.assertEquals(MTI_FIELD_ERR_MSG, e.getHttpError());
54+
Assert.assertFalse(e.isCritical());
55+
Assert.assertNull(e.getInnerException());
56+
Assert.assertEquals("ClientException: (" + HTTP_BAD_REQUEST + ") " + MTI_FIELD_ERR_MSG, e.getLocalizedMessage());
57+
Assert.assertEquals(400, e.getHttpStatusCode());
58+
}
59+
}
1960
}

0 commit comments

Comments
 (0)