This page documents the usage of the Amazon Yojaka Java SDK. The Java SDK provides in-built support for handling the
authentication of your requests using the AWS Sig V4 signing algorithm. It also provides utility classes that handle
the automatic refresh of the LWA access token upon the token’s expiry.
The builder exposes many fluent configuration methods that can be chained to configure a service client. Here’s a simple
example that sets a few optional configuration options and then builds the service client.
By default, a client created by the SDK will connect to the Amazon Yojaka sandbox running in the eu-west-1 region. You
can override the endpoint that the SDK invokes by using the com.amazon.yojaka.util.Endpoints class, and specifying the
endpoint to use while creating the AmazonYojaka client instance.
To connect to the productio instance of Amazon Yojaka in the eu-west-1 region, use the following snippet while creating
the AmazonYojaka client instance.
Each client that you create has its own connection pool. It is recommended to treat the client instances as long-lived objects.
Clients are immutable and thread safe. Clients clean up their resources when garbage collected but if you want to explicitly
shut down the client you can do the following:
importcom.amazon.yojaka.AmazonYojaka;AmazonYojakaamazonYojaka=AmazonYojaka.builder().build();// Use the AmazonYojaka clientamazonYojaka.shutdown();// AmazonYojaka client is now unusable
The AmazonYojaka client natively supports AWS Sig V4 based authentication using AWS IAM credentials. To configure the AWS credentials
used by the client, use the iamCredentials fluent setter.
importcom.amazon.yojaka.AmazonYojaka;importcom.amazonaws.auth.AWSStaticCredentialsProvider;importcom.amazonaws.auth.BasicAWSCredentials;importcom.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;importcom.amazonaws.services.securitytoken.AWSSecurityTokenService;importcom.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;// Create an AWS STS client using the AWS IAM user credentialsAWSSecurityTokenServiceawsSecurityTokenService=AWSSecurityTokenServiceClientBuilder.standard().withRegion(Regions.EU_WEST_1)// Use the access key and secret access key of the AWS IAM user that you have on-boarded with// Amazon Yojaka.withCredentials(newAWSStaticCredentialsProvider(newBasicAWSCredentials("AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY"))).build();// Create an AWS STS assume role credentials provider that will assume the role assigned to your connectorSTSAssumeRoleSessionCredentialsProviderstsAssumeRoleSessionCredentialsProvider=// Use the ARN of the AWS IAM role created by the Amazon Yojaka team as part of on-boardingnewSTSAssumeRoleSessionCredentialsProvider.Builder("AWS_IAM_ROLE_ARN","ConnectorInstanceName")// Use the AWS STS client created above.withStsClient(awsSecurityTokenService)// The maximum duration of the assume role credentials is 4 hours// This credentials provider class will automatically refresh the session credentials upon expiry.withRoleSessionDurationSeconds(4*60*60).build();AmazonYojakaamazonYojaka=AmazonYojaka.builder()// Use the STS assume role credentials provider created above.iamCredentials(stsAssumeRoleSessionCredentialsProvider).build();
The Amazon Yojaka Java SDK provides a utility class to handle the automatic refresh of the LWA access token when it expires.
This class assumes that you have performed all the steps documented in the Authorization section and already have the
LWA refresh token for every seller your connector works with.
The LWA utility class is com.amazon.yojaka.lwa.LWAHandler. It is recommended to create one instance of this class for your
application and use it for the duration of your application running. This class is thread-safe.
Initialize the LWAHandler class with your application’s LWA client_id and client_secret.
importcom.amazon.yojaka.lwa.LWAHandler;// Use your application's client_id and client_secretLWAHandlerlwaHandler=LWAHandler.builder().clientId("amzn1.application-oa2-client.10a...").clientSecret("f477...").build();
To use the LWAHandler class, first register a seller identifier and the LWA refresh token that is associated with the seller.
Note that the seller identifier can be any unique identifier in your connector application that you use to uniquely identify
every seller that your connector works with.
At the time of making an Amazon Yojaka API call, you will need to fetch an LWA access token for the seller on whose behalf
the API call is being invoked. This method will return a cached LWA access token if it is unexpired, and will automatically fetch
and return a new LWA access token, if the currently cached access token is nearing expiry.
The LWAHandler class provided by this Java SDK uses a lazy initialization behaviour. This means that a new LWA
access token is fetched only when needed. This behaviour is used both when the LWA access token is requested the first time
or when the LWA access token is requested the first time after it has expired.
The LWAHandler marks LWA access tokens as expired 55 minutes after they are fetched.
After a client is configured and created, you can make a request to the service. A method on the AmazonYojaka interface
is available for all actions (resource + method) in the Amazon Yojaka API.
For each API method, classes are available that represent the request and response of that API. The request class has fluent
setters for any path parameters, query parameters, headers, and payload model that are defined in the API. The response class
exposes getters for any modeled headers and for the modeled payload.
importcom.amazon.yojaka.AmazonYojaka;importcom.amazon.yojaka.model.UpdateInventoryRequest;importcom.amazon.yojaka.model.UpdateInventoryResult;// This snippet assumes that an AmazonYojaka and LWAHandler instance are available// Create an UpdateInventoryRequest object and invoke the methodUpdateInventoryRequestupdateInventoryRequest=newUpdateInventoryRequest().amzAccessToken(lwaHandler.getAccessToken("sellerId123")).inventoryUpdateSequence("100").locationId("896e68a8-990f-4d5a-994d-f9877a1ef46d").quantity("25000").skuId("TestSku1");UpdateInventoryResultupdateInventoryResult=amazonYojaka.updateInventory(updateInventoryRequest);
importcom.amazon.yojaka.model.ListOrdersRequest;importcom.amazon.yojaka.model.ListOrdersResult;importcom.amazon.yojaka.model.Order;importcom.amazon.yojaka.model.Status;importjava.time.Duration;importjava.time.Instant;importjava.util.List;// This snippet assumes that an AmazonYojaka and LWAHandler instance are available// Create a ListOrdersRequest object and invoke the methodListOrdersRequestlistOrdersRequest=newListOrdersRequest().amzAccessToken(lwaHandler.getAccessToken("sellerId123"))// List all orders received in the past hour.fromTimestamp(String.valueOf(Instant.now().minus(Duration.ofMinutes(60)).toEpochMilli())).locationId("896e68a8-990f-4d5a-9944-f9877a7ef36d")// List only 10 orders.maxResults(String.valueOf(10))// List only those orders which are in the ACCEPTED state.status(Status.ACCEPTED.toString());ListOrdersResultlistOrdersResult=amazonYojaka.listOrders(listOrdersRequest);// The list of orders fetchedList<Order>orders=listOrdersResult.getListOrdersOutput().getOrders();
In addition to client-level configuration that you can specify when creating the AmazonYojaka client, each request class
exposes configuration methods that are scoped to that request alone. Request level configuration takes precedence over
client level configuration.
importcom.amazon.yojaka.model.ConfirmOrderRequest;importcom.amazonaws.opensdk.SdkRequestConfig;amazonYojaka.confirmOrder(newConfirmOrderRequest().amzAccessToken(lwaHandler.getAccessToken("sellerId123")).id("123-abc").sdkRequestConfig(SdkRequestConfig.builder()// Specify request specific timeouts.httpRequestTimeout(1500).totalExecutionTimeout(5000).build()));
In addition to the API modeled data present in result objects, the SDK exposes access to additional HTTP metadata. This
metadata is typically used for debugging issues.
importcom.amazon.yojaka.model.ShipOrderRequest;importcom.amazon.yojaka.model.ShipOrderResult;ShipOrderResultshipOrderResult=amazonYojaka.shipOrder(newShipOrderRequest().amzAccessToken(accessToken).id("123-abc"));// Access the requestId, the underlying HTTP status code or a HTTP header in the responseSystem.out.println(shipOrderResult.sdkResponseMetadata().requestId());System.out.println(shipOrderResult.sdkResponseMetadata().httpStatusCode());shipOrderResult.sdkResponseMetadata().header("Content-Length").ifPresent(System.out::println);
Your connector application must handle all exceptions raised by the AmazonYojaka Java SDK. Service and client exceptions
can be handled separately.
All exceptions that can be thrown by the SDK are a subtype of com.amazonaws.SdkBaseException which is a RuntimeException.
To handle both service and client exceptions in the same way, you can directly catch this exception.
importcom.amazonaws.SdkBaseException;try{amazonYojaka.confirmOrder(...);}catch(SdkBaseExceptione){// All exceptions thrown from the client will be a subtype of SdkBaseException.}
All exceptions modeled in the Amazon Yojaka API will be a sub-type of com.amazon.yojaka.model.AmazonYojakaException. All
service exceptions expose metadata about the HTTP response for logging or debugging purposes.
The following sub-classes of the AmazonYojakaException are available in the com.amazon.yojaka.model package. These
exception classes also provide the exception error code and whether the API is retryable or not as defined in the
Error Handling section.
Client exceptions are modeled as sub-classes of the com.amazonaws.SdkClientException class. This provides greater
granularity to deal with client-side exceptions.
importcom.amazonaws.AboredException;importcom.amazonaws.SdkClientException;importcom.amazonaws.http.timers.client.ClientExecutionTimeoutException;try{amazonYojaka.getInventory(...);}catch(ClientExecutionTimeoutExceptione){// Specific client exception thrown when the totalExecutionTimeout is triggered.}catch(AbortedExceptione){// Thrown when the client thread is interrupted while making a request.}catch(SdkClientExceptione){// All other exceptions can be handled here.}
The SDK provides a utility class to simplify the process of encryption and decryption of any secure data that you exchange
with the Amazon Yojaka APIs. The utility class is com.amazon.yojaka.crypto.CryptoUtils.
This class has methods to decrypt data from the com.amazon.yojaka.model.EncryptedData object returned by an Amazon
Yojaka API response. Similarly, it has methods to encrypt data into a com.amazon.yojaka.model.EncryptedData object
which you can then use in a request to the Amazon Yojaka API.
Tip
Instances of the com.amazon.yojaka.crypto.CryptoUtils class are thread-safe and a single instance can be used
throughout the lifetime of your application.
The methods exposed by this class are:
decrypt - Decrypts encrypted data and returns a byte[]
decryptToString - Decrypts encrypted data and returns a String
encrypt - Encrypts a byte[]
encryptFromString - Encrypts a String
Note
Note that the encryptFromString and decryptToString methods in the com.amazon.yojaka.crypto.CryptoUtils class
assume that the data being encrypted or decrypted is textual data in the UTF-8 character encoding. To decrypt binary data
such as an invoice or a ship-label, use the plain decrypt method that returns a byte[].
importcom.amazon.yojaka.crypto.CryptoUtils;importcom.amazon.yojaka.model.GenerateInvoiceRequest;importcom.amazon.yojaka.model.Invoice;// This snippet assumes that an STAAssumeRoleSessionCredentialsProvider, AmazonYojaka and LWAHandler instance are available// Create a GenerateInvoiceRequest object and invoke the methodInvoiceinvoice=amazonYojaka.generateInvoice(newGenerateInvoiceRequest().amzAccessToken(accessToken).id("123-abc")).getInvoice();// Use the AWS Key Management Service Customer Managed Key ARN that you would have received for every seller that// you work with to decrypt the invoice dataCryptoUtilscryptoUtils=CryptoUtils.builder().awsCredentialsProvider(stsAssumeRoleSessionCredentialsProvider).build();byte[]invoiceData=cryptoUtils.decrypt(sellerKeyArn,invoice.getFileData().getEncryptedContent());
importcom.amazon.yojaka.crypto.CryptoUtils;importcom.amazon.yojaka.model.Context;importcom.amazon.yojaka.model.CreatePackagesRequest;importcom.amazon.yojaka.model.Dimension;importcom.amazon.yojaka.model.LineItem;importcom.amazon.yojaka.model.Package;importcom.amazon.yojaka.model.PackagedLineItemsElement;importcom.amazon.yojaka.model.Weight;// This snippet assumes that an STAAssumeRoleSessionCredentialsProvider, AmazonYojaka and LWAHandler instance are available// Use the AWS Key Management Service Customer Managed Key ARN that you would have received for every seller that// you work with to decrypt the invoice dataCryptoUtilscryptoUtils=CryptoUtils.builder().awsCredentialsProvider(stsAssumeRoleSessionCredentialsProvider).build();EncryptedDataencryptedSerialNumber=cryptoUtils.encryptFromString("SERIAL-NUM-1",sellerKeyArn,newEncryptionInfo().context(Context.SerialNumber).type(Type.AWS_KMS));// Create a CreatePackagesRequest object and set the encrypted serial number within thatamazonYojaka.createPackages(newCreatePackagesRequest().amzAccessToken(accessToken).createPackagesInput(newCreatePackagesInput().packages(newPackage().id("123").length(newDimension().value(10.0).dimensionUnit(DimensionUnit.CM)).width(newDimension().value(20.0).dimensionUnit(DimensionUnit.CM)).height(newDimension().value(30.0).dimensionUnit(DimensionUnit.CM)).weight(newWeight().value(1.0).weightUnit(WeightUnit.Kilograms)).packagedLineItems(newPackagedLineItemsElement().lineItem(newLineItem().id(order.getLineItems().get(0).getId())).quantity(order.getLineItems().get(0).getNumberOfUnits()).serialNumbers(encryptedSerialNumber)))));
Out of the box, the AmazonYojaka client retries on throttling errors (HTTP status code 429) and connection exceptions.
If a different retry policy is desired, a custom one can be set via the client builder.
The easiest way to create a custom retry policy is to use the com.amazonaws.opensdk.retry.RetryPolicyBuilder. It provides
a declarative API to specify when to retry.
importcom.amazonaws.opensdk.retry.RetryPolicyBuilder;importcom.amazon.yojaka.model.InternalServerErrorException;importjava.net.SocketTimeoutException;/** * The policy below will retry if the cause of the failed request matches any of the exceptions * given OR if the HTTP response from the service has one of the provided status codes. */AmazonYojakaamazonYojaka=AmazonYojaka.builder().retryPolicy(RetryPolicyBuilder.standard().retryOnExceptions(InternalServerErrorException.class,SocketTimeoutException.class).retryOnStatusCodes(429,503).maxNumberOfRetries(10).fixedBackoff(100).build()).build();
You can also directly implement the com.amazonaws.retry.v2.RetryPolicy interface to define your own implementation.
com.amazonaws.retry.v2.RetryPolicyContext contains useful metadata about the state of the failed request that can be
used to drive dynamic retry decisions or compute backoff delays.
importcom.amazonaws.retry.v2.RetryPolicy;importcom.amazonaws.retry.v2.RetryPolicyContext;/** * Simple implementation of com.amazonaws.retry.v2.RetryPolicy */publicstaticclassCustomRetryPolicyimplementsRetryPolicy{@OverridepubliclongcomputeDelayBeforeNextRetry(RetryPolicyContextcontext){return100;}@OverridepublicbooleanshouldRetry(RetryPolicyContextcontext){returncontext.retriesAttempted()<3;}}// Using a custom retry policy via the builderAmazonYojakaamazonYojaka=AmazonYojaka.builder().retryPolicy(newCustomRetryPolicy()).build();