Variant Java Client

Variant AIM ServerResourcesDocumentation0.10Clients ⟫ Java

Variant Java Client, User Guide

Release 0.10, May 2019

Related documentation: Variant API JavaDoc.

1Overview

Variant Java client is a library which enables the host application to communicate with Variant AIM server. It exposes server’s functionality in a native Java API and in terms of native Java classes. It can be consumed by any host application, written in Java or another JVM language. It requires Java runtime 8 or higher.

Variant Java client is consumable by any Java program because it makes no assumptions about the technology stack of the host application. This flexibility comes at the expense of some deferred dependencies, such as a mechanism for tracking Variant session ID between state request. These deferred dependencies are provided by stack-specific adapters, as explained further. For Java stacks not (yet) supported by an adapter, application developer can supply a custom implementation.

Most Java Web applications are written on top of the Servlet API. These applications should take advantage of the servlet adapter, which implements deferred dependencies and wraps Variant client in an identical but simplified API, whose deffered methods have been re-written in terms of familiar servlet objects, like HttpServletRequest and HttpServletResponse.

2Installation

Download Variant Java client distribution.

Unpack the distribution:

% unzip /path/to/variant-java-<release>.zip

This will inflate the following artifacts:

File Description
variant-java-client-<release>.jar Variant Java client library. Must be present on the host application’s classpath.
variant-core-<release>.jar Dependent Variant core library. Must be present on the host application’s classpath.

If your Java application is built using a dependency management tool like Maven, you have the following options:

  1. Install into your company’s Maven repository.
    Contact your DevOps for help. Once installed, you can reference them in your aplication’s pom.xml file like so:

    <dependency>
       <groupId>com.variant</groupId>
       <artifactId>java-client</artifactId>
       <version>[0.10,)</version>
    </dependency>
    
    <dependency>
       <groupId>com.variant</groupId>
       <artifactId>variant-core</artifactId>
       <version>[0.10,)</version>
    </dependency>
    
  2. Install into your private Maven repository by typing the following (replacing <release> with the particular version number you’re installing, e.g. 0.10.2):
    % mvn install:install-file -Dfile=/path/to/variant-java-client-<release>.jar \
       -DgroupId=com.variant -DartifactId=java-client -Dversion=<release> -Dpackaging=jar
    
    % mvn install:install-file -Dfile=/path/to/variant-core-<release>.jar \
       -DgroupId=com.variant -DartifactId=variant-core -Dversion=<release> -Dpackaging=jar
    

    You may now reference these artifacts the same was as in the previous paragraph.

  3. Reference the artifacts directly from your file system by placing them in some directory, e.g. lib, and adding the following to your application’s pom.xml file (replacing <release> with the particular version number you’re installing, e.g. 0.10.2):
    <dependency>
       <groupId>com.variant</groupId>
       <artifactId>java-client</artifactId>
       <version>[0.10,)</version>
       <scope>system</scope>
       <systemPath>${project.basedir}/lib/variant-java-<release>.jar
    </dependency>
    
    <dependency>
       <groupId>com.variant</groupId>
       <artifactId>variant-core</artifactId>
       <version>[0.10,)</version>
       <scope>system</scope>
       <systemPath>${project.basedir}/lib/variant-core-<release>.jar
    </dependency>
    
       

Variant Java client has two external transitive dependencies, which are not included in the distribution: Apache HTTP Client (4.5+)  and Simple Logging Facade for Java (1.7+) . If these dependencies aren’t already used by your host application, you may have to add them to your application’s pom.xml file:

<dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
   <version>4.5.1</version>
</dependency>

<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>1.7.12</version>
</dependency>

3Typical Usage Example

NOTE: Typically you will not be consuming Variant client API directly. Rather, your applicatoin will consume a stack-specific adapter, like the servlet adapter, which implements deferred dependencies and wraps Variant client in an identical but simplified API. Refer to the documentation for the Variant client adapter suitable for your technology stack. The remainder of this chapter is only apropriate for the cases when no suitable Variant adapter (yet) exists.

Create an instance of the VariantClient object by using the VariantClient.Builder  class:


VariantClient client = VariantClient.builder()
   .withSessionIdTrackerClass(MyCustomSessionIdTracker.class)
   .build();

Note, that an implementation of the SessionIdTracker  is required.

A re-instantiation of the VariantClient is never required. The host application should hold on to and reuse it for the life of the JVM.

Connect to a variation schema on Variant server:


Connection connection = client.connectTo("variant://myVariantServer.com/myschema");

The Variant connection URI has the following format:

[variant:]//netloc[:port]/schema

The variant protocol spec is optional and may be omitted. If the port is omitted, 5377 is assumed. Thus, localhost/myschema is okay.

The host application should hold on to the connection object and reuse it for all user sessions interested in participating in code variations contained in the given schema. Variant connections are stateless, so they are reusable even after a server restart.

Obtain (or create) a Variant session. They are completely distinct from your host application’s sessions.


// The meaning of userData depends on the environment.
Session session = connection.getOrCreateSession(userData);

The userData argument is a deferred dependency, as discussed in the next section.

Obtain the schema and the state.


Schema schema = session.getSchema();
Optiona loginPage = schema.getState("loginPage");
if (!loginPage.isPresent()) {
   System.out.println("State loginPage is not in the schema. Falling back to control.");
}

Target this session for the state and figure out the live experience(s) the session is targeted for.


ServletStateRequest request = session.targetForState(loginPage.get());
request.getLiveExperiences().forEach(e ->
   System.out.println(
      String.format(
         "We're targeted to experience %s in variation %s", 
         e.getName(), 
         e.getVariation().getName()));
);

At this point, the application can take the code path suitable for the combination of live experience it has been targeted for. Note, that the application does not have to know the names of the variations or experiences to be targeted for a state.

After the host application’s code path is complete, the state request must be committed (if no exceptions were encountered) or failed (if something went awry).


request.commit(userData);  // or .fail(userData)

Here again the userData argument is a deferred dependency and its meaning is explained in the next section. Committing (or failing) a state request triggers the associated state visited trace event with the corresponding completion status.

4Deferred Dependencies

Variant Java client makes no assumptions about host application technology stack or operational details. This generality enables broad applicability: any JVM host application can use it to access Variant server. The price of this generality is that Variant Java client must rely on the application developer to provide certain components at runtime. These are collectively known as deferred dependencies — the subject of this section.

4.1Session ID Tracker

Variant maintains its own sessions, independent from those maintained by the host application. Variant server creates and maintains these sessions, but the client must provide a way of relating two consecutive state requests to the same session. Session ID tracker does exactly that. The session state is kept on Variant server, but the host application is responsible for holding on to the session ID, by which this state can be retrieved.

To fulfill this responsibility, the application developer must supply an implementation of the SessionIdTracker  interface. By contract, an implementation must provide the following public constructor signature and implement the following public methods:

public MyImplClass(Object...)
The constructor Variant uses to instantiate an instance of the session ID tracker within the scope of Connection.getSession(Object...) or Connection.getOrCreateSession(Object...) methods by passing it these arguments without interpretation.
String get()
Retrieves the current value of the session ID from this tracker.
void set(String sessionId)
Sets the value of session ID.
void save(Object...userData)
Saves the currently held session ID to the underlying persistence mechanism. The meaning of userData is up to the implementation: Variant will pass the arguments to the enclosing call to StateRequest.commit(Object...userData) or StateRequest.fail(Object...userData) into this method without interpretation.

The implementing class must be placed on the host application’s classpath and configured via the withTargetingTrackerClass() method . For a sample implementation, see Section 3.4.

4.2API Methods with Deffered Signatures

The following table lists all the methods in Variant Java client whit environment dependent signatures.

Connection.getOrCreateSession(Object...userData) 
Get, if exists, or create, if does not exist, the Variant session with the externally tracked ID. The arguments are passed, without interpretation, to the underlying session ID tracker’s init()  method.
Connection.getSession(Object...userData) 
Get existing Variant session with the externally tracked ID. The arguments are passed, without interpretation, to the underlying session ID tracker’s init()  method.
StateRequest.commit(Object...userData) 
Commit this state request. The arguments are passed, without interpretation, to the underlying session ID tracker’s save()  method.
StateRequest.fail((Object...userData) 
Fail this state request. The arguments are passed, without interpretation, to the underlying session ID tracker’s save()  method.

5Stack-Specific Adapters for Variant Java Client

5.1Servlet Adapter

5.1.1Overview and Installation

Most Java Web applications are written on top of the Servlet API, either directly or via a servlet-based framework, such as Spring. Such applications should take advantage of the servlet adapter to Variant Java client. It is an open source project available directly on GitHub .

The servlet adapter consists of two components:

  • Wrapper client API, which re-writes all deferred method signatures in terms of familiar servlet objects, like HttpServletRequest. See Section 5.1.2 for further details
  • Servlet-based implementation of the session ID tracker, utilizing HTTP cookies. See Section 5.1.3 for details.

To install the servlet adapter for Variant Java client, follow the installation instructions on GitHub.

5.1.2Servlet Adapter Wrapper API

Java web applications, built on top of the servlet API, should communicate with Variant server via the com.variant.client.servlet.* classes, provided by the servlet adapter API , instead of the com.variant.client.* classes, provided by the general purpose Java API . The servlet adapter classes wrap the general purpose classes in a functionally identical API, whose only difference is that it rewrites all deferred environment-dependent method signatures with those that operate on the familiar servlet objects, like HttpServletRequest and HttpServletResponse.

Here’s the typical usage example from Section 3, re-written in terms of the servlet adapter API:

Host application instantiates an instance of ServletVariantClient  from the factory method:


ServletVariantClient client = new ServletVariantClient.Builder().build();

A re-instantiation of the ServletVariantClient is never required. The host application should hold on to and reuse it for the life of the JVM.

Connect to a variation schema on Variant server:


ServletConnection connection = client.connectTo("variant://myVariantServer.com/myschema");

The host application should hold on to the connection object and reuse it for all user sessions interested in participating in code variations contained in the given schema. Variant connections are stateless, so they are reusable even after a server restart.

Obtain (or create) a Variant session. They are completely distinct from your host application’s sessions.


// request is the current HttpServletRequest
ServletSession session = connection.getOrCreateSession(request);

Obtain the schema and the state.


Schema schema = session.getSchema();
Optional loginPage = schema.getState("loginPage");
if (!loginPage.isPresent()) {
   System.out.println("State loginPage is not in the schema. Falling back to control.");
}

Target this session for the state and figure out the live experience(s) the session is targeted for.


ServletStateRequest request = session.targetForState(loginPage.get());
request.getLiveExperiences().forEach(e ->
   System.out.println(
      String.format(
         "We're targeted to experience %s in variation %s", 
         e.getName(), 
         e.getVariation().getName()))
);

At this point, the application can take the code path suitable for the combination of live experience it has been targeted for. Note, that the application does not have to know the names of the variations or experiences to be targeted for a state.

After the host application’s code path is complete, the state request must be committed (if no exceptions were encountered) or failed (if something went awry).


// response is the HttpServletResponse
request.commit(response);  // or .fail(response)

Committing or failing a state request both implicitly trigger the associated state visited trace event with the corresponding completion status.

5.1.3SessionIdTrackerHttpCookie

Servlet adapter for Variant Java client comes with a concrete implementation  of the SessionIdTracker interface . It uses the HTTP cookie mechanism to track Variant session ID between state requests in the session-scoped cookie named variant-ssnid, much like servlet containers use the JSESSIONID cookie to track the HTTP session ID. No action is required to configure this session ID tracker.

5.2Play! Adapter

TBA