RudderStack's Java SDK lets you track and send the events from your Java applications to the specified destinations.
SDK setup requirements
- Sign up to RudderStack Cloud.
- Set up a Java source in your dashboard. You should be able to see a write key for this source, as shown:
You will also need a data plane URL. Refer to the Dashboard Overview guide for more information on the data plane URL and where to find it.
Installing the Java SDK
It is highly recommended to use the Maven build system to add the SDK to your project.
To install the RudderStack Java SDK, add the following lines of code to pom.xml
:
<dependency> <groupId>com.rudderstack.sdk.java.analytics</groupId> <artifactId>analytics</artifactId> <version>3.0.0</version></dependency>
If you're using Gradle, add the following line to your dependencies:
implementation 'com.rudderstack.sdk.java.analytics:analytics:3.0.0'
Initializing the RudderStack client
After installing the SDK, run the following code snippet to initialize the RudderStack client:
RudderAnalytics analytics = RudderAnalytics .builder("<WRITE_KEY>") .setDataPlaneUrl("<DATA_PLANE_URL>") .build();
Migrating from v2 to v3
To migrate to the Java SDK v3.0.0, set the data plane URL using setDataPlaneUrl("<DATA_PLANE_URL>")
(as seen in the above section) instead of passing it as an argument.
Sending events
Unlike the client-side SDKs that deal with only a single user at a given time, the server-side SDKs deal with multiple users simultaneously. Therefore, you must specify either the
userId
or anonymousId
every time while making any API calls supported by the Java SDK.Identify
The identify
call lets you identify a visiting user and associate them to their actions. It also lets you record the traits about them like their name, email address, etc.
A sample identify
call made using the Java SDK is shown below:
analytics.enqueue(IdentifyMessage.builder() .userId("1hKOmRA4GRlm") .traits(ImmutableMap.builder() .put("name", "Alex Keener") .put("email", "alex@example.com") .build() ));
The identify
method parameters are as described below:
Field | Type | Description |
---|---|---|
userId Required, if anonymousId is absent. | String | Unique identifier for a user in your database. |
anonymousId Required, if userId is absent. | String | The SDK automatically sets this identifier in cases where there is no unique identifier for the user. |
context | Object | An optional dictionary of information that provides context about the event. It is not directly related to the API call. |
integrations | Object | An optional dictionary containing the destinations to be enabled or disabled. |
timestamp | Timestamp in ISO 8601 format | The timestamp of the event's arrival. |
traits | Object | An optional dictionary of the user's traits like name or email . |
Track
The track
call lets you record the user actions along with their associated properties. Each user action is called an event.
A sample track
call is shown below:
Map<String, Object> properties = new LinkedHashMap<>(); properties.put("key1", "value1"); properties.put("key2", "value2"); analytics.enqueue( TrackMessage.builder("Java Test") .properties(properties) .anonymousId(anonymousId) .userId(userId));
The track
method parameters are as described below:
Field | Type | Description |
---|---|---|
userId Required, if anonymousId is absent. | String | Unique identifier for a user in your database. |
anonymousId Required, if userId is absent. | String | The SDK automatically sets this identifier in cases where there is no unique identifier for the user. |
event Required | String | Name of the event. |
properties | Object | An optional dictionary of the properties associated with the event. |
context | Object | An optional dictionary of information that provides context about the event. It is not directly related to the API call. |
integrations | Object | An optional dictionary containing the destinations to be enabled or disabled. |
timestamp | Timestamp in ISO 8601 format | The timestamp of the event's arrival. |
Page
The page
call lets you record the page views on your application along with the other relevant information about the page.
A sample page
call is as shown:
analytics.enqueue(PageMessage.builder("Schedule") .userId("1hKOmRA4GRlm") .properties(ImmutableMap.builder() .put("category", "Cultural") .put("path", "/a/b") .build() ));
The page
method parameters are as described below:
Field | Type | Description |
---|---|---|
userId Required, if anonymousId is absent. | String | Unique identifier for a user in your database. |
anonymousId Required, if userId is absent. | String | The SDK automatically sets this identifier in cases where there is no unique identifier for the user. |
name Required | String | Name of the viewed page. |
properties | Object | An optional dictionary of the properties associated with the viewed page, like url or referrer . |
context | Object | An optional dictionary of information that provides context about the event. It is not directly related to the API call. |
integrations | Object | An optional dictionary containing the destinations to be enabled or disabled. |
timestamp | Timestamp in ISO 8601 format | The timestamp of the event's arrival. |
Screen
The screen
call is the mobile equivalent of the page
call. It lets you record the screen views on your mobile app along with other relevant information about the screen.
A sample screen
call is as shown:
analytics.enqueue(ScreenMessage.builder("Schedule") .userId("1hKOmRA4GRlm") .properties(ImmutableMap.builder() .put("category", "Sports") .put("path", "/sports/schedule") .build() ));
The screen
method parameters are as described below:
Field | Type | Description |
---|---|---|
userId Required, if anonymousId is absent. | String | Unique identifier for a user in your database. |
anonymousId Required, if userId is absent. | String | The SDK automatically sets this identifier in cases where there is no unique identifier for the user. |
name Required | String | Name of the viewed screen. |
properties | Object | An optional dictionary of the properties associated with the screen, like url or referrer . |
context | Object | An optional dictionary of information that provides context about the event. It is not directly related to the API call. |
integrations | Object | An optional dictionary containing the destinations to be enabled or disabled. |
timestamp | Timestamp in ISO 8601 format | The timestamp of the event's arrival. |
Group
The group
call lets you link an identified user with a group, such as a company, organization, or an account. It also lets you record any custom traits or properties associated with that group.
A sample group
call made using the Java SDK is shown below:
analytics.enqueue(GroupMessage.builder("group123") .userId("1hKOmRA4GRlm") .traits(ImmutableMap.builder() .put("name", "Rudder") .put("size", 19) .build() ));
The group
method parameters are as follows:
Field | Type | Description |
---|---|---|
userId Required, if anonymousId is absent. | String | Unique identifier for a user in your database. |
anonymousId Required, if userId is absent. | String | The SDK automatically sets this identifier in cases where there is no unique identifier for the user. |
groupId Required | String | Unique identifier of the group in your database. |
traits | Object | An optional dictionary of the group's traits like name or email . |
context | Object | An optional dictionary of information that provides context about the event. It is not directly related to the API call. |
integrations | Object | An optional dictionary containing the destinations to be enabled or disabled. |
timestamp | Timestamp in ISO 8601 format | The timestamp of the event's arrival. |
Alias
The alias
call lets you merge different identities of a known user. It is an advanced method that lets you change the tracked user's ID explicitly. You can use alias
for managing the user's identity in some of the downstream destinations.
alias
events only to select downstream destinations. Refer to the destination-specific documentation for more details.A sample alias
call is as shown:
analytics.enqueue(AliasMessage.builder("previousId") .userId("newId"));
The alias
method parameters are as mentioned below:
Field | Type | Description |
---|---|---|
userId Required, if anonymousId is absent. | String | Unique identifier for a user in your database. |
anonymousId Required, if userId is absent. | String | The SDK automatically sets this identifier in cases where there is no unique identifier for the user. |
previousId Required | String | The previous unique identifier of the user. |
traits | Object | An optional dictionary of the user's traits like name or email . |
context | Object | An optional dictionary of information that provides context about the event. It is not directly related to the API call. |
integrations | Object | An optional dictionary containing the destinations to be enabled or disabled. |
timestamp | Timestamp in ISO 8601 format | The timestamp of the event's arrival. |
Filtering destinations
The Java SDK lets you enable or disable sending events to specifc destinations connected to the source. You can do so by passing the integrations
object in your API calls, as shown:
analytics.enqueue(TrackMessage.builder("Button Clicked") .userId("1hKOmRA4GRlm") .enableIntegration("All", false) .enableIntegration("Amplitude", true));
The above snippet disables sending the event Button Clicked
to any destination except Amplitude.
Context
With the Java SDK, you can send contextual information about the event using the context
object, as shown:
analytics.enqueue(TrackMessage.builder("Button Clicked") .userId("1hKOmRA4GRlm") .context(ImmutableMap.builder() .put("ip", "1.23.45.67") .put("language", "en-uk") .build() ));
The Java SDK also adds the information present in context.library
with every message like name
, version
, etc.
A sample context
object containing the library
information is shown below:
"context": { "library": { "name": "analytics-java", "version": "x.x.x" }}
If you pass any custom information in the context
object, the SDK automatically merges it with the existing context, except the information contained in library
.
Batching events
The RudderStack SDKs are built to support high performance environments. It is safe to use the Java SDK on a web server serving hundreds of requests per second.
Every SDK API you call does not result in a HTTP request but it is queued in the memory instead. RudderStack flushes the events in batches in the background, allowing faster operations.
The Java SDK has a maximum size limit of 500KB per batch request and 32KB per call.
Flushing events
To flush your events, the Java SDK supports the flush
method. It notifies the RudderStack client to upload the events and make sure no events are left in the queue at any given point.
A sample snippet highlighting the use of the flush
method is shown below:
analytics.flush()
Blocking flush
By default, the Java SDK does not support blocking flush implicitly. You need to create a BlockingFlush
class (handles a maximum of 65535 parallel calls to flush) or a TierBlockingFlush
class (no limit on parallel calls) depending on your requirement.
BlockingFlush
and TierBlockingFlush
classes are not a part of the core Java SDK.A sample snippet highlighting the use of BlockingFlush
is shown below:
final BlockingFlush blockingFlush = BlockingFlush.create();
RudderAnalytics analytics = RudderAnalytics .builder("<WRITE_KEY>") .plugin(blockingFlush.plugin()) .setDataPlaneUrl("<DATA_PLANE_URL>") .build();
// ...YOUR CODE...
analytics.flush(); // Triggers a flush.blockingFlush.block();analytics.shutdown(); // Shuts down after the flush is complete.
A detailed implementation of the BlockingFlush
class is shown below. Note that this is just a sample code snippet and you can modify it as per your use case.
package sample;
import com.rudderstack.sdk.java.analytics.RudderAnalytics;import com.rudderstack.sdk.java.analytics.Callback;import com.rudderstack.sdk.java.analytics.MessageTransformer;import com.rudderstack.sdk.java.analytics.Plugin;import com.rudderstack.sdk.java.analytics.messages.Message;import com.rudderstack.sdk.java.analytics.messages.MessageBuilder;import java.util.concurrent.Phaser;
/* * The {@link RudderAnalytics} class doesn't come with a blocking {@link RudderAnalytics#flush()} implementation * out of the box. It's trivial to build one using a {@link Phaser} that monitors requests and is * able to block until they're uploaded. */public class BlockingFlush {
public static BlockingFlush create() { return new BlockingFlush(); }
BlockingFlush() { this.phaser = new Phaser(1); }
final Phaser phaser;
public Plugin plugin() { return builder -> { builder.messageTransformer( builder1 -> { phaser.register(); return true; });
builder.callback( new Callback() { @Override public void success(Message message) { phaser.arrive(); }
@Override public void failure(Message message, Throwable throwable) { phaser.arrive(); } }); }; }
public void block() { phaser.arriveAndAwaitAdvance(); }}
TierBlockingFlush
section below.TierBlockingFlush
To remove the limitations on the maximum number of supported parties, you can use the TierBlockingFlush
class.
The following snippet highlights its use:
final TierBlockingFlush blockingFlush = TierBlockingFlush.create();
RudderAnalytics analytics = RudderAnalytics .builder("<WRITE_KEY>") .plugin(blockingFlush.plugin()) .setDataPlaneUrl("<DATA_PLANE_URL>") .build();
// ...YOUR CODE...
analytics.flush(); // Trigger a flush.blockingFlush.block();analytics.shutdown(); // Shut down after the flush is complete.
The following snippet highlights a detailed implementation of the TierBlockingFlush
class with support for more than 65535 parties. Note that this is just a sample code snippet and you can modify it as per your use case.
package sample;
import com.rudderstack.sdk.java.analytics.Callback;import com.rudderstack.sdk.java.analytics.Plugin;import com.rudderstack.sdk.java.analytics.messages.Message;
import java.util.concurrent.Phaser;
/** * Blocking flush implementor for cases where parties exceed 65535 */public class TierBlockingFlush {
private static final int MAX_PARTIES_PER_PHASER = (1 << 16) - 2; // max a phaser can accommodate
public static TierBlockingFlush create() { return new TierBlockingFlush(MAX_PARTIES_PER_PHASER); }
private TierBlockingFlush(int maxPartiesPerPhaser) { this.currentPhaser = new Phaser(1); this.maxPartiesPerPhaser = maxPartiesPerPhaser; }
private Phaser currentPhaser; private final int maxPartiesPerPhaser;
public Plugin plugin() { return builder -> { builder.messageTransformer( messageTransformationBuilder -> { currentPhaser = currentPhaser.getRegisteredParties() == maxPartiesPerPhaser ? new Phaser(currentPhaser) : currentPhaser; currentPhaser.register(); return true; });
builder.callback( new Callback() { @Override public void success(Message message) { onResult(); }
@Override public void failure(Message message, Throwable throwable) { onResult(); }
private void onResult() { if (currentPhaser.getUnarrivedParties() == 0) { currentPhaser = currentPhaser.getParent(); } currentPhaser.arrive(); } }); }; }
public void block() { currentPhaser.arriveAndAwaitAdvance(); }}
Logging
To see the data that is sent over HTTP when debugging any issues, enable the SDK's verbose logging feature.
- Refer to the sample snippet for more information on setting the logs using the Java SDK.
- Refer to the sample app for more information on using the logging plugin during the SDK initialization.
Gzipping requests
The Java SDK automatically gzips requests. It also lets you do so using interceptors in OkHttp.
To disable the Gzip feature using the setGZIP
API while initializing the SDK, run the following snippet:
RudderAnalytics analytics = RudderAnalytics .builder("<WRITE_KEY>") .setDataPlaneUrl("<DATA_PLANE_URL>") .setGZIP(false) .build();
Note that if you pass the OkHttp client using the client
API while initializing your SDK, then it is preferred over the default Gzip behavior. It means that even if you use the setGZIP
API to enable/disable Gzip requests, the behavior will be determined based on the interceptor passed in the OkHttp client.
FAQ
Can I use the ImmutableMap
class?
Yes, you can use the ImmutableMap
class via the Guava library or use the Java maps.
How do I flush events on demand?
To flush your events on demand, call the flush
method as shown:
analytics.flush()
Contact us
For more information on the topics covered on this page, email us or start a conversation in our Slack community.