Coder Social home page Coder Social logo

currencycloud-java's People

Contributors

anouskastreets avatar chrisrosecc avatar dependabot[bot] avatar derikvercueil avatar drosser avatar eaddario avatar emmajiugo avatar falconetpt avatar francescobbo avatar gergelykovacs avatar gyorgy-marbella avatar iaptekar avatar jeremy-cc avatar jonathancouchman avatar kvndevelops avatar lizzie88 avatar mmazi avatar rjnienaber avatar vitalykhamidullin avatar yogeshing avatar yorikim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

currencycloud-java's Issues

Only update changed attributes

When updating a resource (POST /{id}), the Ruby library seems to (I don't speak Ruby) send only the properties that were actually updated (or skip the updated altogether if there were none). Should the Java library do this too?

Beneficiary Date of Birth

The problem is related to create/update/validate beneficiary;
When I provide the beneficiary’s date of birth value I always have the following error:
“Invalid extra parameters:‘{:“beneficiary_date_of_birth\tdate”=>“1986-12-12"}’”

I think there is a bug in the sdk because in the CurrencyCloud.class the field beneficiaryDateOfBirth is binded with the value “beneficiary_date_of_birth date” where I think should be only “beneficiary_date_of_birth”.
The methods are : ” createBeneficiary(…), validateBeneficiary(…), updateBeneficiary(…)

The API "authorisePayment" responses me 400

@org.junit.Test public void authroizePayment() { List<String> paymentIdList = new ArrayList<>(); paymentIdList.add("fec2f9c4-7f92-4d11-ac96-39e441aafbc9"); PaymentAuthorisations paymentAuthorisations = currencyCloud.authorisePayment(paymentIdList); for (PaymentAuthorisation paymentAuthorisation : paymentAuthorisations.getPaymentAuthorisations()) { System.err.println(paymentAuthorisation.toString()); } }

response
`BadRequestException

platform: "Java 1.8.0_202 (Oracle Corporation)"
request:
parameters:
paymentIds[]: "fec2f9c4-7f92-4d11-ac96-39e441aafbc9"
verb: "post"
url: "https://devapi.currencycloud.com/v2/payments/authorise?paymentIds[]=fec2f9c4-7f92-4d11-ac96-39e441aafbc9"
response:
status_code: 400
date: "Tue, 16 Jul 2019 06:51:10 GMT"
request_id: "c03afe4d-7884-4cfc-9b3c-1e6aa51cb665"
error_code: "invalid_extra_parameters"
errors:

  • field: "base"
    code: "invalid_extra_parameters"
    message: "Invalid extra parameters:'{:paymentIds=>["fec2f9c4-7f92-4d11-ac96-39e441aafbc9"
    ]}'"
    params:
    parameters:
    paymentIds:
    - "fec2f9c4-7f92-4d11-ac96-39e441aafbc9"`

How can I deal with this? Is there the server or SDK error? I use sdk 3.2.2

Typesafe enumerations

There are many parameters/properties in the API that are declared as Strings, but can take a limited set of values (eg. country codes, currencies, custom statuses etc.). See if it makes sense to implement them as enums. Going with Strings for the time being.

Client-side validation

It seems many things could be validated on the client. Bean validation could be used for this.

Create a PGP/GPG key that will be used to sign the final artifacts (jars) to be published

Please read the detailed instructions. This is a short summary of what needs to be done:

gpg --gen-key
gpg --list-keys
gpg2 --edit-key C6EED57A # delete subkeys if necessary -- please see the detailed instructions
> key 1
> delkey
gpg --keyserver hkp://pool.sks-keyservers.net --send-keys C6EED57A # replace with your keyid from --list-keys

This is only needed for final releases (we can publish snapshots unsigned).

The GPG key will be used to sign artifacts on a local machine (where the key is stored); I'll provide instructions to do this later. Eg., the GPG passphrase will need to be saved locally in Maven's ~/.m2/settings.xml.

Create Account SDK throws exception - despite successful execution

I am facing issue in
currencyCloudClient.createAccount(account)
It is throwing exception (exception attached below) and despite on the demo account it creates a sub account.
I am using this file src/test/java/com/currencycloud/examples/CurrencyCloudCookbook.java

And added these few lines of code.

Code

Account account = client.createAccount(new Account(
    "Coockbook",
    "company",
    "BLR",
    "BLR",
    "560068",
    "IN"
    )
);

Exception

Exception in thread "main" ---
exception_type: "UnexpectedException"
platform: "Java 18.0.1.1 (Oracle Corporation)"
request: null
inner_error: "java.lang.reflect.InaccessibleObjectException: Unable to make protected\
  \ final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)\
  \ throws java.lang.ClassFormatError accessible: module java.base does not \"opens\
  \ java.lang\" to unnamed module @307f6b8c"

	at com.currencycloud.client.ExceptionTransformer.aroundInvoke(ExceptionTransformer.java:23)
	at si.mazi.rescu.InterceptedInvocationHandler.invoke(InterceptedInvocationHandler.java:42)
	at com.currencycloud.client.Reauthenticator.aroundInvoke(Reauthenticator.java:23)
	at si.mazi.rescu.InterceptedInvocationHandler.invoke(InterceptedInvocationHandler.java:42)
	at jdk.proxy2/jdk.proxy2.$Proxy4.createAccount(Unknown Source)
	at com.currencycloud.client.CurrencyCloudClient.createAccount(CurrencyCloudClient.java:157)
	at com.currencycloud.examples.CurrencyCloudCookbook.runCookBook(CurrencyCloudCookbook.java:51)
	at com.currencycloud.examples.CurrencyCloudCookbook.main(CurrencyCloudCookbook.java:30)

Screenshot of account created from the dashboard

Screenshot 2022-07-29 at 1 22 10 PM

Should some/all model classes support hashCode() and equals()?

Model classes have not been designed to support object equality, and we want to find out if that would be useful to sdk users.

An immediate benefit would be proper and efficient manipulation in collections. Are there other use cases? Should all or some classes support it? If some, which?

Create documentation

The starting point could be the README.md. If it's big, it could be split into wiki pages on GitHub.

Also provide:

  • some well-documented code samples (eg. in tests),
  • javadocs,
  • descriptive annotations,
  • developer docs (for people updating this library in the future)

UnexpectedError

The Ruby library has a notion of UnexpectedError. Should we mimic this in the Java library?

Exceptions retrieving Swift payment tracking info

When calling CurrencyCloudClient.getPaymentTrackingInfo, we get the following error generated:


exception_type: "ApiException"
platform: "Java 17.0.4 (Eclipse Adoptium)"
request:
  parameters: {}
  verb: "get"
  url: "https://api.currencycloud.com/v2/payments/<payment-id>/tracking_info"
response:
  status_code: 200
  date: null
  request_id: null
error_code: null
errors: []
at com.currencycloud.client.exception.ApiException.create(ApiException.java:71)
  at com.currencycloud.client.ExceptionTransformer.aroundInvoke(ExceptionTransformer.java:19)
  at si.mazi.rescu.InterceptedInvocationHandler.invoke(InterceptedInvocationHandler.java:42)
  at com.currencycloud.client.Reauthenticator.aroundInvoke(Reauthenticator.java:23)
  at si.mazi.rescu.InterceptedInvocationHandler.invoke(InterceptedInvocationHandler.java:42)
  at jdk.proxy2/jdk.proxy2.$Proxy24.getPaymentTrackingInfo(Unknown Source)
  at com.currencycloud.client.CurrencyCloudClient.getPaymentTrackingInfo(CurrencyCloudClient.java:1057)
  at com.bndrts.service.external.CurrencycloudWrapper.lambda$getPaymentTrackingInfo$7(CurrencycloudWrapper.java:124)
  at com.bndrts.service.external.CurrencycloudWrapper.lambda$callOnBehalfOf$11(CurrencycloudWrapper.java:154)
  at com.currencycloud.client.CurrencyCloudClient.onBehalfOfDo(CurrencyCloudClient.java:121)
  at com.bndrts.service.external.CurrencycloudWrapper.callOnBehalfOf(CurrencycloudWrapper.java:151)
  at com.bndrts.service.external.CurrencycloudWrapper.getPaymentTrackingInfo(CurrencycloudWrapper.java:123)
  at com.bndrts.service.external.CCPaymentInstructionService.paymentReceived(CCPaymentInstructionService.java:226)
  at com.bndrts.service.external.CCPaymentInstructionService.processPayment(CCPaymentInstructionService.java:211)
  at java.base/java.util.ArrayList.forEach(Unknown Source)
  at com.bndrts.service.external.CCPaymentInstructionService.processCompletedSwiftPayments(CCPaymentInstructionService.java:203)
  at com.bndrts.service.external.SwiftPaymentReceivedJob.execute(SwiftPaymentReceivedJob.java:24)
  at com.bndrts.messaging.consumer.ScheduledEventHandler.lambda$execute$1(ScheduledEventHandler.java:43)
  at com.bndrts.service.infrastructure.MDCPropagatingExecutorServiceDecorator.lambda$withMDC$0(ExecutorFactory.java:152)
  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
  at java.base/java.lang.Thread.run(Unknown Source)
Caused by: ResponseException{errorCode='null', errorMessages=null}
  at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
  at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
  at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Unknown Source)
  at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source)
  at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:123)
  at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createUsingDefault(StdValueInstantiator.java:278)
  at com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer.deserializeFromObject(ThrowableDeserializer.java:145)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:184)
  at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
  at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
  at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
  at si.mazi.rescu.serialization.jackson.JacksonResponseReader.read(JacksonResponseReader.java:53)
  at si.mazi.rescu.serialization.jackson.JacksonResponseReader.readException(JacksonResponseReader.java:58)
  at si.mazi.rescu.ResponseReader.read(ResponseReader.java:82)
  at si.mazi.rescu.RestInvocationHandler.mapInvocationResult(RestInvocationHandler.java:175)
  at si.mazi.rescu.RestInvocationHandler.receiveAndMap(RestInvocationHandler.java:163)
  at si.mazi.rescu.RestInvocationHandler.invoke(RestInvocationHandler.java:119)
  at com.currencycloud.client.AutoAuthenticator.aroundInvoke(AutoAuthenticator.java:26)
  at si.mazi.rescu.InterceptedInvocationHandler.invoke(InterceptedInvocationHandler.java:42)
  at com.currencycloud.client.ExceptionTransformer.aroundInvoke(ExceptionTransformer.java:17)
  ... 20 more

Digging a bit deeper, the root cause seems to be that we're getting an empty array for the charge_amount field in our payment_events:

{
    "uetr": "...",
    "transaction_status": {
        "status": "completed",
        "reason": null
    },
    "initiation_time": "2022-09-06T00:16:20+00:00",
    "completion_time": "2022-09-06T07:52:57+00:00",
    "last_update_time": "2022-09-06T07:53:17+00:00",
    "payment_events": [
        {
            "tracker_event_type": "customer_credit_transfer_payment_status_update",
            "valid": true,
            "transaction_status": {
                "status": "completed",
                "reason": null
            },
            "funds_available": "2022-09-06T07:52:00+00:00",
            "forwarded_to_agent": null,
            "from": "...",
            "to": "...",
            "originator": "...",
            "serial_parties": {
                "debtor": null,
                "debtor_agent": null,
                "intermediary_agent1": null,
                "instructing_reimbursement_agent": null,
                "creditor_agent": null,
                "creditor": null
            },
            "sender_acknowledgement_receipt": "2022-09-06T07:52:57+00:00",
            "instructed_amount": null,
            "confirmed_amount": {
                "currency": "USD",
                "amount": "..."
            },
            "interbank_settlement_amount": null,
            "interbank_settlement_date": null,
            "charge_amount": [],
            "charge_type": null,
            "foreign_exchange_details": null,
            "last_update_time": "2022-09-06T07:53:17+00:00"
        },
...

The SDK is attempting to deserialise this empty array to a com.currencycloud.client.model.PaymentTrackingInfo.Amount object which then fails with a Jackson exception:

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `com.currencycloud.client.model.PaymentTrackingInfo$Amount` from Array value (token `JsonToken.START_ARRAY`)

Serialize collections correctly

Currently collections are serialized like this:

payment_types=regular,another

but should be like this:

payment_types[]=regular&payment_types[]=another

Payment creation missing payer country and address (for USD / CAD payments)

/** Create a Payment */
@post
@path("payments/create")
@consumes(MediaType.APPLICATION_FORM_URLENCODED)
Payment createPayment(
@HeaderParam("X-Auth-Token") String authToken,
@FormParam("currency") String currency,
@FormParam("beneficiary_id") String beneficiaryId,
@FormParam("amount") BigDecimal amount,
@FormParam("reason") String reason,
@FormParam("reference") String reference,
@nullable @FormParam("payment_date") java.sql.Date paymentDate,
@nullable @FormParam("payment_type") String paymentType,
@nullable @FormParam("conversion_id") String conversionId,
@nullable @FormParam("payer_entity_type") String payerEntityType,
@nullable @FormParam("payer_company_name") String payerCompanyName,
@nullable @FormParam("payer_first_name") String payerFirstName,
@nullable @FormParam("payer_last_name") String payerLastName,
@nullable @FormParam("payer_city") String payerCity,
@nullable @FormParam("payer_postcode") String payerPostcode,
@nullable @FormParam("payer_state_or_province") String payerStateOrProvince,
@nullable @FormParam("payer_date_of_birth") java.sql.Date payerDateOfBirth,
@nullable @FormParam("payer_country") String payerCountry,
@nullable @FormParam("payer_address[]") List payerAddress,
@nullable @FormParam("payer_identification_type") String payerIdentificationType,
@nullable @FormParam("payer_identification_value") String payerIdentificationValue,
@nullable @FormParam("on_behalf_of") String onBehalfOf
) throws ResponseException;

public Payment createPayment(Payment payment, @nullable Payer payer) throws CurrencyCloudException {
if (payer == null) {
payer = Payer.create();
}
return api.createPayment(authToken,
payment.getCurrency(),
payment.getBeneficiaryId(),
payment.getAmount(),
payment.getReason(),
payment.getReference(),
dateOnly(payment.getPaymentDate()),
payment.getPaymentType(),
payment.getConversionId(),
payer.getLegalEntityType(),
payer.getCompanyName(),
payer.getFirstName(),
payer.getLastName(),
payer.getCity(),
payer.getPostcode(),
payer.getStateOrProvince(),
dateOnly(payer.getDateOfBirth()),
payer.getCountry(),
payer.getAddress(),
payer.getIdentificationType(),
payer.getIdentificationValue(),
onBehalfOf
);
}

Validate beneficiary missing beneficiary address (for USD / CAD payments)

@post
@path("beneficiaries/validate")
@consumes(MediaType.APPLICATION_FORM_URLENCODED)
Beneficiary validateBeneficiary(
@HeaderParam("X-Auth-Token") String authToken,
@FormParam("bank_country") String bankCountry,
@FormParam("currency") String currency,
@FormParam("beneficiary_country") String beneficiaryCountry,
@nullable @FormParam("account_number") String accountNumber,
@nullable @FormParam("routing_code_type_1") String routingCodeType1,
@nullable @FormParam("routing_code_value_1") String routingCodeValue1,
@nullable @FormParam("routing_code_type_2") String routingCodeType2,
@nullable @FormParam("routing_code_value_2") String routingCodeValue2,
@nullable @FormParam("bic_swift") String bicSwift,
@nullable @FormParam("iban") String iban,
@nullable @FormParam("bank_address[]") List bankAddress,
@nullable @FormParam("bank_name") String bankName,
@nullable @FormParam("bank_account_type") String bankAccountType,
@nullable @FormParam("beneficiary_entity_type") String beneficiaryEntityType,
@nullable @FormParam("beneficiary_company_name") String beneficiaryCompanyName,
@nullable @FormParam("beneficiary_first_name") String beneficiaryFirstName,
@nullable @FormParam("beneficiary_last_name") String beneficiaryLastName,
@nullable @FormParam("beneficiary_city") String beneficiaryCity,
@nullable @FormParam("beneficiary_postcode") String beneficiaryPostcode,
@nullable @FormParam("beneficiary_address[]") List beneficiaryAddress,
@nullable @FormParam("beneficiary_state_or_province") String beneficiaryStateOrProvince,
@nullable @FormParam("beneficiary_date_of_birth date") Date beneficiaryDateOfBirth,
@nullable @FormParam("beneficiary_identification_type") String beneficiaryIdentificationType,
@nullable @FormParam("beneficiary_identification_value") String beneficiaryIdentificationValue,
@nullable @FormParam("payment_types[]") List paymentTypes,
@nullable @FormParam("on_behalf_of") String onBehalfOf
) throws ResponseException;

public Beneficiary validateBeneficiary(Beneficiary beneficiary) throws CurrencyCloudException {
return api.validateBeneficiary(
authToken,
beneficiary.getBankCountry(),
beneficiary.getCurrency(),
beneficiary.getBeneficiaryCountry(),
beneficiary.getAccountNumber(),
beneficiary.getRoutingCodeType1(),
beneficiary.getRoutingCodeValue1(),
beneficiary.getRoutingCodeType2(),
beneficiary.getRoutingCodeValue2(),
beneficiary.getBicSwift(),
beneficiary.getIban(),
beneficiary.getBankAddress(),
beneficiary.getBankName(),
beneficiary.getBankAccountType(),
beneficiary.getBeneficiaryEntityType(),
beneficiary.getBeneficiaryCompanyName(),
beneficiary.getBeneficiaryFirstName(),
beneficiary.getBeneficiaryLastName(),
beneficiary.getBeneficiaryCity(),
beneficiary.getBeneficiaryPostcode(),
beneficiary.getBeneficiaryAddress(),
beneficiary.getBeneficiaryStateOrProvince(),
beneficiary.getBeneficiaryDateOfBirth(),
beneficiary.getBeneficiaryIdentificationType(),
beneficiary.getBeneficiaryIdentificationValue(),
beneficiary.getPaymentTypes(),
onBehalfOf
);
}

Provide some example client code

Creating some example code / tutorial, like an implementation of the Cookbook, might be appropriate.

I already have the code, but need to decide on a good way to provide this. I see two options:

  1. A simple separate project, eg. currencycloud-java-example -- this would also showcase any necessary configuration (eg. maven dependencies),
  2. A test class, eg. something like DemoServerTest.

Solution 1. Is cleaner and more informative for beginners, with less clutter (like testing annotations), but is a bit harder to maintain (eg. SDK refactorings might break the example). 2. is simpler to maintain (automatically refactored when the SDK is refactored) and also provides some value to development as it can be used as a regression test.

Register the project with Sonatype

This is what needs to be done to publish the project artifacts (#21). The detailed instructions can be found at http://central.sonatype.org/pages/ossrh-guide.html#initial-setup .

This is required for both snapshot and final releases.

  1. Create a Sonatype Jira account
  2. Open a Jira issue; provide the following details:

If "artifact id" is requested, the value is currencycloud-java. If PGP (public) keys are requested, see #27.

The account username and password will need to be encrypted and provided to Travis -- I'll provide instructions for this later.

Exclude logback configuration

Hello CurrencyCloud team :),

You should consider removing logback.xml from the package and instead letting users configure their own logging.

  1. Library users might not use logback,
  2. Library users might want to have their own logback config (eg. structured JSON logging).

I encountered an issue when setting up a Spring Boot app which didn't appear to log anything, traced it to currencycloud-java. :)

Push the snapshot artifacts to the Sonatype repo

Sonatype credentials (the same as for their Jira) are required to push the artifacts to the Maven repository.

Please test that this works.

You need Java 7+ SDK, git and Maven 3 installed.

Add the credentials to ~/.m2/settings.xml:

<settings>
  ...
  <servers>
    <server>
      <id>ossrh</id>
      <username>your-jira-id</username>
      <password>your-jira-pwd</password>
    </server>
  </servers>
  ...
</settings>

Then run this:

git clone [email protected]:CurrencyCloud/currencycloud-java.git
cd currencycloud-java
mvn clean deploy

Provide encrypted credentials for Sonatype

For Travis to push snapshot artifacts to the Sonaype maven repo automatcially (#30), it needs to know the CurrencyCloud's Sonatype credentials. Travis provides a way to supply these credentials in an encrypted form publicly (via the GitHub repo) so that only Travis can access them. Instructions how to do this follow. The easiest (described below) uses the Travis Ruby gem.

Please note that if the Sonatype password (or username) contains symbols with special meaning in shell scripting such as braces, parentheses, backslashes, and pipe symbols, these must be escaped as described here.

gem install travis  # use sudo if necessary
cd currencycloud-java  # the project code directory
travis encrypt CI_DEPLOY_USERNAME=<CurrencyCloud Sonatype username>
travis encrypt CI_DEPLOY_PASSWORD=<CurrencyCloud Sonatype password>

The last two commands will output something like secure: ".... encrypted data ....". Please copy both of these values and paste them here.

Provide a more readable API

Some of the method calls in the current Java API are a bit verbose and not easy to read; provide a better alternative.

Example of current API call:

    Beneficiary beneficiary = client.updateBeneficiary(
            "081596c9-02de-483e-9f2a-4cf55dcdf98c", "Test User 2",
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null,
            null, null, "Acme Inc.", null, null, "Manchester", 
            null, null, null, null, null, null
    );

A possible alternative:

    Beneficiary beneficiary = client.updateBeneficiary(
            new Beneficiary.Builder()
                    .Id("081596c9-02de-483e-9f2a-4cf55dcdf98c")
                    .BankAccountHolderName("Test User 2")
                    .BeneficiaryCompanyName("Acme Inc.")
                    .BeneficiaryCity("Manchester")
                    .build());

Missing terms_and_conditions_accepted param for account

Hey all,

Currently in prod we are getting: 400 Terms and conditions accepted is required when creating a new sub-account.

We seem to be missing the terms_and_conditions_accepted needed in order to create an account per:

terms_and_conditions_accepted formData boolean Acceptance of the terms and conditions. Required for sub-accounts that are on our Outsourced KYC model, optional otherwise.

https://developer.currencycloud.com/docs/item/create-account/

Probably need to add it here:

public Account createAccount(Account account) throws CurrencyCloudException {
return api.createAccount(
authToken,
userAgent,
account.getAccountName(),
account.getLegalEntityType(),
account.getStreet(),
account.getCity(),
account.getPostalCode(),
account.getCountry(),
account.getStateOrProvince(),
account.getBrand(),
account.getYourReference(),
account.getStatus(),
account.getSpreadTable(),
account.getIdentificationType(),
account.getIdentificationValue(),
account.getApiTrading(),
account.getOnlineTrading(),
account.getPhoneTrading()
);
}

But will leave that to you all 😄

Thanks,
Hasnain
Lightyear

On Behalf Of functionality not thread safe

The Java SDK is not thread safe for on behalf calls. This means ownership of actions carried out on the API using on behalf functionality could be wrong in a multi-threaded setup.

The aim is to update the on behalf call so it is thread safe and still supports creating one client across multiple workers.

Here is some unit test code to show collisions when multiple threads try to access a single instance of "CurrencyCloudClient.onBehalfDo" (credit: @derikvercueil)

package com.currencycloud.client;

import org.junit.Assert;
import org.junit.Test;

/**
 *
 * Class to simulate multiple threads accessing a single CurrencyCloudClient instance.
 *
 */
class TestThread extends Thread {

    private final String contactId;
    private final int threadId;
    private final CurrencyCloudClient client;

    public TestThread(int threadId, CurrencyCloudClient client, String contactId) {
        this.client = client;
        this.threadId = threadId;
        this.contactId = contactId;
    }

    @Override
    public void run() {
        System.out.println("TestThread \"" + threadId + "\" running.");
        int i=0;
        while (i<1000) {
            try {
                client.onBehalfOfDo(contactId, new Runnable() {

                    @Override
                    public void run() {
                        String onBehalfOf = client.getOnBehalfOf();
                        /**
                         * It is always expected that \"threadId\" = 2nd last character of onBehalf to ensure that the Runnable for that Thread is running
                         */
                        Assert.assertEquals("\"client.onBehalfOfDo\" Runnable executing for thread \"" + threadId + "\". \"onBehalfOf\"=\"" + onBehalfOf + "\"", threadId, Integer.parseInt(onBehalfOf.substring(onBehalfOf.length() - 2, onBehalfOf.length() - 1)));
                    }
                });
                i++;
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }
    }
}

/**
 * Test multiple threads attempting to access a single instance of the CurrencyCloudClient class.
 *
 */
public class CurrencyCloudClientConcurrencyTest {

    protected CurrencyCloudClient client = new CurrencyCloudClient("http://localhost:5555", null, null);

    @Test
    public void testConcurrentAccess() throws Exception {

        TestThread[] threads = new TestThread[10];
        int counter = 0;
        for (TestThread thread : threads) {
            thread = new TestThread(counter, client, "f57b2d33-652c-4589-a8ff-7762add270" + counter + "d");
            thread.start();
            counter++;
        }

    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.