Websocket client for Centrifugo server and Centrifuge library.
There is no v1 release of this library yet – API still evolves. At the moment patch version updates only contain backwards compatible changes, minor version updates can have backwards incompatible API changes.
Check out client SDK API specification to learn how this SDK behaves. It's recommended to read that before starting to work with this SDK as the spec covers common SDK behavior - describes client and subscription state transitions, main options and methods. Also check out examples folder.
The features implemented by this SDK can be found in SDK feature matrix.
The latest
centrifuge-javais compatible with Centrifugo server v6, v5 and v4 and Centrifuge >= 0.25.0. For Centrifugo v2, Centrifugo v3 and Centrifuge < 0.25.0 you should usecentrifuge-javav0.1.0.
Library available in Maven: https://search.maven.org/artifact/io.github.centrifugal/centrifuge-java
This library depends on streamsupport-minifuture. In case your project has a dependency to streamsupport-cfuture and you have a class name conflicts, you can exclude minifuture safely. Example for Gradle:
implementation('io.github.centrifugal:centrifuge-java:{version}') {
exclude group: 'net.sourceforge.streamsupport', module: 'streamsupport-minifuture'
}
http://www.javadoc.io/doc/io.github.centrifugal/centrifuge-java
Centrifuge-java library uses Protobuf library (Lite version) for client protocol. This fact and the fact that Protobuf Lite uses reflection internally can cause connection errors when releasing your application with Android shrinking and obfuscation features enabled. See protocolbuffers/protobuf#6463 for details. To deal with this centrifuge-java comes with Proguard rules included in the jar.
More information about Android shrinking.
Centrifuge-java pulls in com.google.protobuf:protobuf-javalite. If your app already depends on the full com.google.protobuf:protobuf-java (commonly via gRPC, Firebase, or another library), the two artifacts contain overlapping classes and the build will fail with Duplicate class com.google.protobuf.* errors, or — worse — the app builds but a NoSuchMethodError / NoSuchFieldError is thrown at runtime when the SDK builds its protobuf messages, leaving the connection in a stale state with no obvious reason in logs.
If you hit this, exclude the other library's bundled protobuf so that the lite version provided by centrifuge-java is the only one on the classpath:
implementation('com.example:other-library:x.y.z') {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
exclude group: 'com.google.protobuf', module: 'protobuf-lite'
}Starting in 0.6.0, an Error (e.g. NoSuchMethodError) thrown from generated protobuf code is surfaced through the SDK's onError event and disconnects with code bad protocol, instead of silently failing in the executor — so a misconfigured classpath produces an actionable stack trace.
See example code in console Java example or in demo Android app
To use with Android don't forget to set INTERNET permission to AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Each Client runs its work on one thread of its own: connection and subscription logic, timers, and the calls of
your EventListener and SubscriptionEventListener methods and of result callbacks. So listeners and callbacks are
never called concurrently, and they are called in the order things happened.
- Client methods are asynchronous.
connect(),disconnect(),subscribe(),unsubscribe(),publish()and the other calls queue their work on that thread and return at once. A call made from a listener takes effect after the listener returns. - Don't block in a listener or callback. While it runs, the client handles nothing else: no replies, no pings, no
timers. Waiting inside a listener for the result of a call (e.g. on a future completed by a callback) can't
succeed before the call's timeout, and
close(awaitMilliseconds)called from a listener can't wait for the client thread it runs on: it returnsfalseafter the wait. Move blocking work to your own thread. - Exceptions. An exception thrown by a listener or callback is reported through
EventListener.onError(UnclassifiedError) and doesn't stop the client's own work. An exception thrown while the client handles data from the server — e.g. by anonConnected,onSubscribedoronPublicationlistener — also disconnects the client with code 2 ("bad protocol"), as centrifuge-js does; callconnect()to start again. A token or state getter that throws is treated as if it called back with that error. - Call
close()when the client is no longer needed.disconnect()keeps the client's thread for a laterconnect().
When a mobile application goes to the background there are OS-specific limitations for established persistent connections - which can be silently closed shortly. Thus in most cases you need to disconnect from a server when app moves to the background and connect again when app goes to the foreground.
Library is available under the MIT license. See LICENSE for details.
This section contains an information for library contributors. You don't need generating protobuf code if you just want to use centrifuge-java in your project.
The protobuf definitions are located in centrifuge/main/proto directory.
Protocol class is generated automatically during project compilation by protobuf gradle plugin.
The unit tests run with no external services:
./gradlew :centrifuge:testIntegration tests exercise the SDK against a real Centrifugo server brought up via docker-compose.yml. The repo's Makefile wraps both:
make test-unit # unit tests only
make test-integration # starts Centrifugo, then runs integration tests
make test # both
make down # stop Centrifugo when doneThe Gradle wrapper is pinned at 7.5.1, which supports JDK 8–17. The Makefile auto-discovers openjdk@17 from a Homebrew install (brew install openjdk@17); set JAVA_HOME yourself if you'd rather pick the JDK explicitly. CI runs both jobs on Ubuntu — see .github/workflows/ci.yml.
We use metalava-gradle to ensure we are aware of breaking API changes in the library.
All PRs check API signatures for compatibility. If you see an error, it may indicate there is a breaking change.
Regenerate API signature with the following command and include an updated api.txt in your PR:
./gradlew :centrifuge:metalavaGenerateSignatureAlso indicate a breaking change in changelog.
To verify API compatibility locally, run the following command:
./gradlew :centrifuge:metalavaCheckCompatibility- Bump version in
publish-setup.gradle. - Create new library tag.
The release GitHub Action should now publish the library.
Do all steps from the automatic publishing. Create configuration file gradle.properties in GRADLE_USER_HOME:
signing.keyId=<LAST_8_SYMBOLS_OF_KEY_ID>
signing.password=<PASSWORD>
signing.secretKeyRingFile=/Path/to/.gnupg/secring.gpg
mavenCentralUsername=<USERNAME>
mavenCentralPassword=<PASSWORD>Then run:
./gradlew publish --no-daemon --no-parallel
./gradlew closeAndReleaseRepositoryThe second command will promote staged release to production on MavenCentral.
You can do it manually by following the instructions:
https://central.sonatype.org/pages/releasing-the-deployment.html
I.e.
- Login here: https://oss.sonatype.org/
- Go to
Staging repositories - Find release, push
Closebutton, wait - Push
Releasebutton
- Thanks to Maks Atygaev for initial library boilerplate