The Java and Kotlin SDK for Ably Pub/Sub: publish and subscribe to channels, and use message history, presence and push notifications, from servers, Android apps and JVM desktop clients.
The SDK is published as two packages, one for each side of the connection. You choose the package by where your code runs, and build your client through that package's entry point.
| Package | Use it for | Entry point |
|---|---|---|
io.ably.pubsub:server |
Servers and other trusted backend environments | PubSubServer.realtimeClientBuilder(), PubSubServer.httpClientBuilder() |
io.ably.pubsub:device |
Android apps and other end-user runtimes, including JVM desktop apps | PubSubDevice.clientBuilder() |
Java 8 or later (the JVM, plus Android 7.0 / API level 24 or later for io.ably.pubsub:device).
Use exactly one of io.ably.pubsub:server or io.ably.pubsub:device in an app.
dependencies {
implementation("io.ably.pubsub:server:2.0.0")
}On Android and on the JVM, Gradle picks the right variant of io.ably.pubsub:device:
dependencies {
implementation("io.ably.pubsub:device:2.0.0")
}Maven can't pick the variant, so a JVM desktop app names device-jvm directly:
<dependency>
<groupId>io.ably.pubsub</groupId>
<artifactId>device-jvm</artifactId>
<version>2.0.0</version>
</dependency>Use an API key on a server. The samples read it from the ABLY_API_KEY environment variable; set it
to an API key from your Ably dashboard. The
following code connects, subscribes to a channel, publishes a message to it and waits for the
message to arrive.
import java.util.concurrent.CompletableFuture;
import io.ably.pubsub.realtime.Channel;
import io.ably.pubsub.realtime.PubSubRealtimeClient;
import io.ably.pubsub.server.PubSubServer;
import io.ably.pubsub.types.Message;
public class ServerQuickStart {
public static void main(String[] args) throws Exception {
String ablyApiKey = System.getenv("ABLY_API_KEY");
PubSubRealtimeClient realtime = PubSubServer.realtimeClientBuilder()
.key(ablyApiKey)
.build();
// Subscribing attaches the channel. A message published while connecting is queued.
Channel channel = realtime.channels.get("test-channel");
CompletableFuture<Message> received = new CompletableFuture<>();
channel.subscribe(received::complete);
channel.publish("test-event", "hello world");
System.out.println("Received: " + received.get().data);
realtime.close();
}
}To publish over HTTP without a realtime connection, build an HTTP client instead:
import io.ably.pubsub.http.PubSubHttpClient;
import io.ably.pubsub.server.PubSubServer;
import io.ably.pubsub.types.AblyException;
public class ServerPublish {
public static void main(String[] args) throws AblyException {
String ablyApiKey = System.getenv("ABLY_API_KEY");
PubSubHttpClient http = PubSubServer.httpClientBuilder()
.key(ablyApiKey)
.build();
http.channels.get("test-channel").publish("test-event", "hello world");
}
}Important
Server clients must authenticate with an API key. To use a token instead, see Server clients and token authentication.
The following endpoint issues token requests. Devices set authUrl to its URL, for example
https://your-server.example.com/ably-auth:
package com.example;
import io.ably.pubsub.http.Auth;
import io.ably.pubsub.http.PubSubHttpClient;
import io.ably.pubsub.server.PubSubServer;
import io.ably.pubsub.types.AblyException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class AblyAuthServer {
private final PubSubHttpClient http;
public AblyAuthServer() throws AblyException {
String ablyApiKey = System.getenv("ABLY_API_KEY");
http = PubSubServer.httpClientBuilder()
.key(ablyApiKey)
.build();
}
@GetMapping(value = "/ably-auth", produces = MediaType.APPLICATION_JSON_VALUE)
public String ablyAuth() throws AblyException {
// Authenticate the caller first. Grant only the operations this user needs.
String userId = "me"; // the signed-in user's ID, from your session
Auth.TokenParams params = new Auth.TokenParams();
params.clientId = userId;
// Without a capability, the token gets all of the API key's capabilities.
params.capability = "{\"test-channel\":[\"publish\",\"subscribe\",\"history\"]}";
return http.auth.createTokenRequest(params, null).asJson();
}
public static void main(String[] args) {
SpringApplication.run(AblyAuthServer.class, args);
}
}See token authentication for how devices use it.
Authenticate a device with tokens issued by your own server, such as the endpoint above, through
authUrl or authCallback.
Never embed an API key in an app that runs on an end-user device.
import io.ably.pubsub.device.PubSubDevice;
import io.ably.pubsub.realtime.Channel;
import io.ably.pubsub.realtime.PubSubRealtimeClient;
import io.ably.pubsub.types.AblyException;
public class DeviceQuickStart {
public static void main(String[] args) throws AblyException {
PubSubRealtimeClient realtime = PubSubDevice.clientBuilder()
.authUrl("https://your-server.example.com/ably-auth")
.clientId("me")
.build();
// The client connects automatically. Subscribing attaches the channel.
Channel channel = realtime.channels.get("test-channel");
channel.subscribe(message -> System.out.println("Received: " + message.data));
channel.publish("test-event", "hello world");
}
}To get tokens from your own code instead of a URL, pass an Auth.TokenCallback to
authCallback(...). It can return a token string, a TokenDetails or a TokenRequest.
Version 2.0 splits the SDK into a server package and a device package, and builds clients with builders.
| 1.x | 2.0 | |
|---|---|---|
| Package | io.ably:ably-java, io.ably:ably-android |
io.ably.pubsub:server or io.ably.pubsub:device |
| Java package | io.ably.lib.* |
io.ably.pubsub.* |
| Realtime client | new AblyRealtime(options) |
PubSubServer.realtimeClientBuilder()…build() or PubSubDevice.clientBuilder()…build() |
| HTTP client | new AblyRest(options) |
PubSubServer.httpClientBuilder()…build() |
See UPGRADING.md for all other changes.
Set options on the builders, or pass a ClientOptions object to realtimeClientBuilder(options),
httpClientBuilder(options) or clientBuilder(options).
endpoint selects the Ably host for HTTP requests and the realtime connection. It replaces the
1.x environment, restHost and realtimeHost options.
String ablyApiKey = System.getenv("ABLY_API_KEY");
PubSubRealtimeClient realtime = PubSubServer.realtimeClientBuilder()
.key(ablyApiKey)
.endpoint("nonprod:sandbox")
.build();- Never put a port in
endpoint:localhost:8080is not a hostname and does not resolve. - Set the port with
tlsPort, or withportwhen TLS is off. - With TLS off (
tls(false)), use token authentication.
See Choose the host with endpoint for how the
old options map to endpoint.
The packages use OkHttp for HTTP and WebSocket connections. To use the built-in
HttpURLConnection engine instead, swap the engine. For example, on a server:
dependencies {
implementation("io.ably.pubsub:server:2.0.0") {
exclude(group = "io.ably.pubsub", module = "network-client-okhttp")
}
runtimeOnly("io.ably.pubsub:network-client-default:2.0.0")
}Pass a ProxyOptions object to the builder to connect through a proxy.
import io.ably.pubsub.http.HttpAuth;
import io.ably.pubsub.types.ProxyOptions;
ProxyOptions proxy = new ProxyOptions();
proxy.host = "your-proxy-host";
proxy.port = 8080;
// Only if the proxy requires authentication.
proxy.username = "your-username";
proxy.password = "your-password";
proxy.prefAuthType = HttpAuth.Type.BASIC;
String ablyApiKey = System.getenv("ABLY_API_KEY");
PubSubRealtimeClient realtime = PubSubServer.realtimeClientBuilder()
.key(ablyApiKey)
.proxy(proxy)
.build();Set logLevel to a constant from io.ably.pubsub.util.Log: VERBOSE, DEBUG, INFO, WARN
(the default), ERROR or NONE. Log lines go to standard output unless you set a logHandler.
import io.ably.pubsub.util.Log;
String ablyApiKey = System.getenv("ABLY_API_KEY");
PubSubRealtimeClient realtime = PubSubServer.realtimeClientBuilder()
.key(ablyApiKey)
.logLevel(Log.DEBUG)
.logHandler((severity, tag, msg, tr) -> System.err.println(tag + ": " + msg))
.build();Ably LiveObjects ships as a separate plugin,
io.ably.pubsub:liveobjects, that works with either package. Add it at runtime and the client finds
it on the classpath.
dependencies {
runtimeOnly("io.ably.pubsub:liveobjects:2.0.0")
}Attach the channel with the object_subscribe and object_publish modes to use objects on it. See
the Java quickstart and the
example app.
io.ably.pubsub:deviceneeds Android API level 24. Apps that support API levels 19 to 23 must stay on 1.x (io.ably:ably-android) for now.
- Getting started with Pub/Sub using Java
- Ably Pub/Sub documentation and examples
- Token authentication
- UPGRADING.md: migrating from 1.x
- CHANGELOG.md: release notes; all Ably releases are listed on changelog.ably.com
- examples/: the LiveObjects example app
Read CONTRIBUTING.md before contributing; it also describes the release process.
For help, visit Ably's support page. Report bugs and compatibility problems on GitHub Issues.
Licensed under the Apache License, Version 2.0. See LICENSE.
