Press "Enter" to skip to content

Variant CVM Java Client

Release 1.0.1, December 2023

1. Overview

Variant Java client is a library which enables a host application written in a JVM language to communicate with the Variant CVM server. It exposes server’s functionality in terms of intuitive 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. For example, a web application written running in a servlet container, or one with an embedded Netty or Akka HTTP servers, or even a Java program using a call center routing API can all take advantage of code variations managed by the Variant server.

This great flexibility is achieved through the use of two dependency mechanisms:

  • Session ID tracker is an implementation of the SessionIdTracker interface. It allows Variant client to seamlessly track the session ID via a mechanism inherent in the host application’s tech stack. For example, HTTP applications use cookies and mobile clients use the phone ID.
  • Opaque deferred dependencies. Some API methods accept opaque arguments named userData of type Object. Variant client passes these directly to the user-provided implementation of Session ID tracker without any interpretation.

2. Installation

  • [todo] Download the Variant Java client distribution. 
  • Unpack the distribution:
% unzip /path/to/variant-java-<release>.zip

This will inflate the following artifacts:

FileDescription
variant-java-client-<release>.jarVariant Java client library. Must be present on the host application’s classpath.
variant-core-<release>.jarDependent 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 application’s pom.xml file like so:

<dependency> 
  <groupId>com.variant</groupId> 
  <artifactId>java-client</artifactId> 
  <version>[1.0,)</version> 
</dependency> 
<dependency> 
  <groupId>com.variant</groupId> 
  <artifactId>variant-core</artifactId> 
  <version>[1.0,)</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. 1.0.1):

$ 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 

Now you ca 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. 1.0.1):

<dependency> 
  <groupId>com.variant</groupId> 
  <artifactId>java-client</artifactId> 
  <version>[1.0,)</version> 
  <scope>system</scope> 
  <systemPath>${project.basedir}/lib/variant-java-<release>.jar</systemPath>
</dependency> 
<dependency> 
  <groupId>com.variant</groupId> 
  <artifactId>variant-core</artifactId> 
  <version>[1.0,)</version> 
  <scope>system</scope> 
  <systemPath>${project.basedir}/lib/variant-core-<release>.jar</systemPath>
</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>

3. Developing with Variant Java Client

3.1 The Structure of a Variant Session

A host application communicates with Variant server via a native client library, supplied by Variant and suitable for the application’s language. Variant release 0.10 ships with a fully functional Java client and a partial JavaScript client, suitable for deployment to Web browsers.

A typical interaction between the host application and the Variant server goes through this logic

3.2 Typical Usage Example

VariantClient client = VariantClient.build(
  builder -> {
    builder.withSessionIdTrackerClass(MyCustomSessionIdTracker.class);
  });

Note, that an implementation of the SessionIdTracker  is required. This custom class encapsulates the mechanism for tracking user sessions. In most cases, one of the the pre-defined session ID trackers in com.variant.client.sid will suffice.

You will only need one instance of VariantClient per address space.

  • Connect to a variation schema on Variant server:
Connection connection =
  client.connectTo("http://myVariantServer.com:5377/myschema");

The Variant connection URI is an HTTP URI because the network protocol used by the Variant server tunnels all communication via HTTP/S. 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; 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.

3.3 Concurrent API Calls

TBD

3.4. Trace Events

Trace events can be triggered implicitly by Variant server or explicitly by client code. The only implicitly triggered trace event is the state visited event, which signifies that a user session visited a particular state. (Refer to the Server User Guide for details on how Variant models interactive appliations.) State visited event is created by the Session.targetForState()  method and is accessible via the StateRequest.getStateVisitedEvent()  method. The host application can add custom event attributes to it, which will be serialized with the event by the event flusher. The state visited event is triggered when the host applicatoin either commits   or fails  the request. 

4. Deferred 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.1. Session 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.2. API Methods with Deferred 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.