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 main entry point class of the Java SDK is the com.amazon.yojaka.AmazonYojaka class.
To create a client, use the client builder. You can obtain an instance of the builder via a static factory method located on the client interface.
import com.amazon.yojaka.AmazonYojaka;
AmazonYojakaClientBuilder builder = AmazonYojaka.builder();
AmazonYojaka amazonYojaka = builder.build();
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.
import com.amazon.yojaka.AmazonYojaka;
import com.amazonaws.opensdk.config.ConnectionConfiguration;
import com.amazonaws.opensdk.config.TimeoutConfiguration;
AmazonYojaka amazonYojaka = AmazonYojaka.builder()
.connectionConfiguration(new ConnectionConfiguration()
.maxConnections(100)
.connectionMaxIdleMillis(1000))
.timeoutConfiguration(new TimeoutConfiguration()
.httpRequestTimeout(3000)
.totalExecutionTimeout(10000)
.socketTimeout(2000))
.build();
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.
import com.amazon.yojaka.AmazonYojaka;
import com.amazon.yojaka.util.Endpoints;
AmazonYojaka amazonYojaka = AmazonYojaka.builder()
.endpoint(Endpoints.url(Endpoints.STAGE_PRODUCTION, Endpoints.REGON_EU_WEST_1))
.build();
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:
import com.amazon.yojaka.AmazonYojaka;
AmazonYojaka amazonYojaka = AmazonYojaka.builder().build();
// Use the AmazonYojaka client
amazonYojaka.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.
import com.amazon.yojaka.AmazonYojaka;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
// Create an AWS STS client using the AWS IAM user credentials
AWSSecurityTokenService awsSecurityTokenService = 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(new AWSStaticCredentialsProvider(
new BasicAWSCredentials("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 connector
STSAssumeRoleSessionCredentialsProvider stsAssumeRoleSessionCredentialsProvider =
// Use the ARN of the AWS IAM role created by the Amazon Yojaka team as part of on-boarding
new STSAssumeRoleSessionCredentialsProvider.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();
AmazonYojaka amazonYojaka = AmazonYojaka.builder()
// Use the STS assume role credentials provider created above
.iamCredentials(stsAssumeRoleSessionCredentialsProvider)
.build();
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.
Below are some usage examples.
import com.amazon.yojaka.AmazonYojaka;
import com.amazon.yojaka.model.UpdateInventoryRequest;
import com.amazon.yojaka.model.UpdateInventoryResult;
// This snippet assumes that an AmazonYojaka and LWAHandler instance are available
// Create an UpdateInventoryRequest object and invoke the method
UpdateInventoryRequest updateInventoryRequest = new UpdateInventoryRequest()
.amzAccessToken(lwaHandler.getAccessToken("sellerId123"))
.inventoryUpdateSequence("100")
.locationId("896e68a8-990f-4d5a-994d-f9877a1ef46d")
.quantity("25000")
.skuId("TestSku1");
UpdateInventoryResult updateInventoryResult = amazonYojaka.updateInventory(updateInventoryRequest);
import com.amazon.yojaka.model.ListOrdersRequest;
import com.amazon.yojaka.model.ListOrdersResult;
import com.amazon.yojaka.model.Order;
import com.amazon.yojaka.model.Status;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
// This snippet assumes that an AmazonYojaka and LWAHandler instance are available
// Create a ListOrdersRequest object and invoke the method
ListOrdersRequest listOrdersRequest = new ListOrdersRequest()
.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());
ListOrdersResult listOrdersResult = amazonYojaka.listOrders(listOrdersRequest);
// The list of orders fetched
List<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.
import com.amazon.yojaka.model.ConfirmOrderRequest;
import com.amazonaws.opensdk.SdkRequestConfig;
amazonYojaka.confirmOrder(new ConfirmOrderRequest()
.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.
import com.amazon.yojaka.model.ShipOrderRequest;
import com.amazon.yojaka.model.ShipOrderResult;
ShipOrderResult shipOrderResult = amazonYojaka.shipOrder(new ShipOrderRequest()
.amzAccessToken(accessToken)
.id("123-abc"));
// Access the requestId, the underlying HTTP status code or a HTTP header in the response
System.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.
import com.amazonaws.SdkBaseException;
try {
amazonYojaka.confirmOrder(...);
} catch (SdkBaseException e) {
// 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.
try {
amazonYojaka.createPackages(...);
} catch (AmazonYojakaException e) {
int statusCode = e.sdkHttpMetadata().httpStatusCode();
String requestId = e.sdkHttpMetadata().requestId();
Optional<String> contentLength = e.sdkHttpMetadata().header("Content-Length");
ByteBuffer responseContent = e.sdkHttpMetadata().responseContent();
}
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.
BadRequestException
ConflictException
ForbiddenException
InternalServerErrorException
NotFoundException
ServiceUnavailableException
UnauthorizedException
Client exceptions are modeled as sub-classes of the com.amazonaws.SdkClientException class. This provides greater granularity to deal with client-side exceptions.
import com.amazonaws.AboredException;
import com.amazonaws.SdkClientException;
import com.amazonaws.http.timers.client.ClientExecutionTimeoutException;
try {
amazonYojaka.getInventory(...);
} catch (ClientExecutionTimeoutException e) {
// Specific client exception thrown when the totalExecutionTimeout is triggered.
} catch (AbortedException e) {
// Thrown when the client thread is interrupted while making a request.
} catch (SdkClientException e) {
// 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[].
Below are some usage examples.
import com.amazon.yojaka.crypto.CryptoUtils;
import com.amazon.yojaka.model.GenerateInvoiceRequest;
import com.amazon.yojaka.model.Invoice;
// This snippet assumes that an STAAssumeRoleSessionCredentialsProvider, AmazonYojaka and LWAHandler instance are available
// Create a GenerateInvoiceRequest object and invoke the method
Invoice invoice = amazonYojaka.generateInvoice(new GenerateInvoiceRequest()
.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 data
CryptoUtils cryptoUtils = CryptoUtils.builder()
.awsCredentialsProvider(stsAssumeRoleSessionCredentialsProvider)
.build();
byte[] invoiceData = cryptoUtils.decrypt(sellerKeyArn, invoice.getFileData().getEncryptedContent());
import com.amazon.yojaka.crypto.CryptoUtils;
import com.amazon.yojaka.model.Context;
import com.amazon.yojaka.model.CreatePackagesRequest;
import com.amazon.yojaka.model.Dimension;
import com.amazon.yojaka.model.LineItem;
import com.amazon.yojaka.model.Package;
import com.amazon.yojaka.model.PackagedLineItemsElement;
import com.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 data
CryptoUtils cryptoUtils = CryptoUtils.builder()
.awsCredentialsProvider(stsAssumeRoleSessionCredentialsProvider)
.build();
EncryptedData encryptedSerialNumber = cryptoUtils.encryptFromString("SERIAL-NUM-1", sellerKeyArn,
new EncryptionInfo()
.context(Context.SerialNumber)
.type(Type.AWS_KMS));
// Create a CreatePackagesRequest object and set the encrypted serial number within that
amazonYojaka.createPackages(new CreatePackagesRequest()
.amzAccessToken(accessToken)
.createPackagesInput(new CreatePackagesInput()
.packages(new Package()
.id("123")
.length(new Dimension()
.value(10.0)
.dimensionUnit(DimensionUnit.CM))
.width(new Dimension()
.value(20.0)
.dimensionUnit(DimensionUnit.CM))
.height(new Dimension()
.value(30.0)
.dimensionUnit(DimensionUnit.CM))
.weight(new Weight()
.value(1.0)
.weightUnit(WeightUnit.Kilograms))
.packagedLineItems(new PackagedLineItemsElement()
.lineItem(new LineItem()
.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.
import com.amazonaws.opensdk.retry.RetryPolicyBuilder;
import com.amazon.yojaka.model.InternalServerErrorException;
import java.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.
*/
AmazonYojaka amazonYojaka = 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.
import com.amazonaws.retry.v2.RetryPolicy;
import com.amazonaws.retry.v2.RetryPolicyContext;
/**
* Simple implementation of com.amazonaws.retry.v2.RetryPolicy
*/
public static class CustomRetryPolicy implements RetryPolicy {
@Override
public long computeDelayBeforeNextRetry(RetryPolicyContext context) {
return 100;
}
@Override
public boolean shouldRetry(RetryPolicyContext context) {
return context.retriesAttempted() < 3;
}
}
// Using a custom retry policy via the builder
AmazonYojaka amazonYojaka = AmazonYojaka.builder()
.retryPolicy(new CustomRetryPolicy())
.build();