Expand my Community achievements bar.

Join us for an upcoming in-person Adobe Target Skill Builders event ~~> We're hosting these live learning opportunities to equip you with the knowledge and skills to leverage Target successfully. Learn more to see if we'll be coming to a city near you!

Server-Side Optimization with the new Target Java SDK

Avatar

Administrator

8/18/22

Author: Anshul Chauhan

We are excited to announce the release of our entirely new Adobe Target Java SDK. Java, being one of the most utilized server-side programming languages, is highly leveraged by our customers. We want our customers to focus more on their business logic rather than the technicalities of integrating with our APIs. Below, we provide our server-side integration goals and best practices to optimize the performance of your Java applications when integrating with our Target Java SDK.

What’s Target Java SDK?

The Adobe Target Java SDK helps you execute a server-side integration on JVM based architectures using the Target Delivery API. It helps you retrieve and deliver personalized experiences in a highly performant manner. Furthermore, the Java SDK helps manage integrations with Adobe Experience Cloud solutions like the Experience Cloud Visitor ID and Adobe Analytics.

There are certain goals that should always be kept in mind while integrating Target on server-side. Let’s see what these are and how Target Java SDK can help you achieve them.

A sample e-commerce app integrating Target as a service using the Target Java SDKA sample e-commerce app integrating Target as a service using the Target Java SDK

Achieving server-side goals using Target Java SDK

Goal 1: Improve performance by reducing network chattiness

Discrete requests for a user should be collapsed into a single request using batching on server-side. The benefit is that the device then pays the price of network latency once and leverages the low latency and more powerful hardware on server-side. Using Target Java SDK you can combine mbox and views request for both pre-fetch and execute in a single request. For example:

 

 

 

MboxRequest m1 = new MboxRequest().name("mbox1").index(1);
MboxRequest m2 = new MboxRequest().name("mbox2").index(2);

TargetDeliveryRequest request= TargetDeliveryRequest.builder()
        .context(new Context().channel(ChannelType.WEB))
        .execute(new ExecuteRequest().mboxes(Arrays.asList(m1)))
        .prefetch(new PrefetchRequest().mboxes(Arrays.asList(m2)))
        .build();

TargetDeliveryResponse response = targetClient.getOffers(request);

 

 

 

Full sample: https://github.com/adobe/target-java-sdk#target-only

Goal 2: Improve performance by embracing server-side parallelism

This should be achieved without each engineer needing to become an expert in low-level threading, synchronization, thread safety, concurrent data structures, non-blocking IO and other such concerns. Target Java SDK handles all the complexities of multithreading for you, you just need to configure it as per your needs.

 

 

 

ClientConfig clientConfig = ClientConfig.builder()
        .client("emeaprod4")
        .organizationId("0DD934B85278256B0A490D44@AdobeOrg")
        .maxConnectionsPerHost(100)
        .maxConnectionsTotal(200)
        .connectTimeout(10000)
        .socketTimeout(10000)
        .logRequestStatus(true)
        .build();
TargetClient targetClient = TargetClient.create(clientConfig);

 

 

 

Full sample: https://git.corp.adobe.com/TnT/target-java-sdk#creating-client

Goal 3: Improve page load time

Make sure the client (eg. at.js) doesn’t have to make any calls to Target before rendering the content. This way the client does not block or pre-hide any section of the page resulting in faster page loads and preventing intermittent flickering. For example, if the client is browser with at.js, you just need to set the target response in a variable called serverState and at.js will render the response without making any calls to the Target server.

 

 

 

//Server
TargetDeliveryResponse response =targetClient.getOffers(request);
model.addAttribute("serverState", response.getServerState());

//Client
<script>
    window.targetGlobalSettings = {
        serverState: ${serverState}
    }
</script>

 

 

 

Full sample: https://github.com/adobe/target-java-sdk#atjs-integration-via-serverstate

Goal 4: Maintain sessions whenever possible

The biggest challenge while integrating Target on server-side is maintaining sessions. Target Java SDK makes it easy by returning cookies along with target response. These are not actual cookies but some key/value pairs that Target needs to maintain sessions and communicate to Target edge cluster. Always make sure that these cookies are provided in subsequent calls for the same user. Target will keep refreshing the cookies, so make sure cookie values are updated in every call. These can be saved on client-side or server-side. For example, if client is browser these can be saved in client cookies:

 

 

 

HttpServletRequest request;
HttpServletResponse response;
//Pass cookies in request
TargetDeliveryRequest dRequest = TargetDeliveryRequest.builder()
                .context(getContext(request))
                .execute(executeRequest)
                .cookies(getTargetCookies(request.getCookies()))
                .build();
//Save cookies in response
TargetDeliveryResponse targetResponse = targetJavaClient.getOffers(targetDeliveryRequest);
setCookies(targetResponse.getCookies(), response);

 

 

 

Full sample: https://github.com/adobe/target-java-sdk#maintaining-sessions

Goal 5: Minimise Target latency impact

Since Target calls are independent in most cases, server integrations should make asynchronous calls to Target before preparing the content for the page to be returned. For example, DB or other network calls should be made after Target request (made asynchronously). By the time your other requests complete, Target response should also be available and total latency will be a max of(I/O calls, Target). This could be easily achieved by using Target Java SDK since it supports asynchronous calls. For example:

 

 

 

TargetDeliveryRequest targetDeliveryRequest = TargetDeliveryRequest.builder()
                    .context(getContext(request))
                    .execute(executeRequest)
                    .cookies(getTargetCookies(request.getCookies()))
                    .build();
CompletableFuture<TargetDeliveryResponse> targetResponseAsync =
        targetJavaClient.getOffersAsync(targetDeliveryRequest);
targetResponseAsync.thenAccept(tr -> setCookies(tr.getCookies(), response));
simulateIO();
TargetDeliveryResponse targetResponse = targetResponseAsync.join();

 

 

 

Full sample: https://github.com/adobe/target-java-sdk#asynchronous-requests

Best practices for Target Java SDK

  1. Use single TargetClient object per application lifetime: TargetClient is optimized to be used as a single instance per application lifetime. For example, TargetClient can be created as a spring bean. If your application does not support dependency injection, consider creating a static provider or singleton class to provide TargetClient instance in the application.
  2. Run queries asynchronously for higher throughput: Target Java SDK supports making asynchronous calls. As mentioned in the goals of server-side integration, consider making async calls whenever possible. This reduces the overall latency by the common denominator of Target and your network calls.
  3. Log requests or request status for debugging: Target Java SDK supports logging the request/response as well as their status for debugging purposes. It requires an implementation of slf4j logger. Make sure to change the logger mode to DEBUG.
  4. Tune SDK parameters for better performance: By default Target Java SDK uses reasonable defaults for concurrency and timeout(default 10 seconds). You don’t need to reduce the maxConnections (default 200) since this number will never be reached if you don’t have enough traffic. But feel free to tune these numbers to get maximum performance for your use case.
  5. Close the TargetClient on graceful shutdown: It’s always a good practice to close the closable resources on shutdown. TargetClient will do the necessary cleanup required when it is closed.

Java SDK is open sourced

Now, I know you are excited about our new Java SDK and its capabilities. But what’s more exciting is that you can contribute to the Java SDK since it is open sourced! But, before contributing to the Java SDK, we highly encourage you to familiarize yourself with the documentation and code. We also provided sample apps and demos that you can explore, which will in turn help expedite the familiarization process. If you feel comfortable with the code and you already see opportunities for improvement, follow our contribution guidelines outlined in the CONTRIBUTING.md file. Next, submit a pull request through GitHub. Congratulations! You now have the bragging rights to being an open-source contributor!

Originally published: Nov 1, 2019