Note that this is a FORK of a project by NaikSoftware! This version was originally made to avoid using RetroLambda.
(It now has many other important differences.)
This library provides support for STOMP protocol over Websockets.
Right now, the library works as a client for any backend that supports STOMP, such as Node.js (e.g. using StompJS) or Spring Boot (with WebSocket support).
Add library as gradle dependency (Versioning info here):
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
dependencies {
compile 'com.github.forresthopkinsa:StompProtocolAndroid:17.11.0'
}
You can use this library two ways:
- Using the old JACK toolchain
- If you have Java 8 compatiblity and Jack enabled, this library will work for you
- Using the new Native Java 8 support
- It should work in all Android Studio (and Gradle plugin) 3.0.0+ versions
- You can find compatibility info on the Releases Page
However, this fork is NOT compatible with Retrolambda. If you have RL as a dependency, then you should be using the upstream version of this project!
Finally, please take bugs and questions to the Issues page! I'll try to answer within one business day.
WebSocketConfig.java
@Configuration
@EnableWebSocket
@EnableWebSocketMessageBroker
class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// We're using Spring's built-in message broker, with prefix "/topic"
config.enableSimpleBroker("/topic");
// This is the prefix for client requests
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/example-endpoint");
}
}
SocketController.java
@RestController
class SocketController {
@MessageMapping('/hello')
@SendTo('/topic/greetings')
public String greeting(String name) {
return "Hello, " + name + "!";
}
}
Check out the upstream example server
Basic usage
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
// ...
StompClient client = Stomp.over(Stomp.ConnectionProvider.OKHTTP, "http://localhost/example-endpoint");
client.connect();
client.topic("/topic/greetings").subscribe(message -> {
Log.i(TAG, "Received message: " + message.getPayload());
});
client.send("/app/hello", "world").subscribe(
() -> Log.d(TAG, "Sent data!"),
error -> Log.e(TAG, "Encountered error while sending data!", error)
);
// ...
// close socket connection when finished or exiting
client.disconnect();
See the upstream example
Method Stomp.over
uses an enum to know what connection provider to use.
Currently supported connection providers:
Stomp.ConnectionProvider.JWS
('org.java-websocket:Java-WebSocket:1.3.2')Stomp.ConnectionProvider.OKHTTP
('com.squareup.okhttp3:okhttp:3.8.1')
You can add own connection provider. Just implement interface ConnectionProvider
.
If you implement new provider, please create pull request :)
Subscribe lifecycle connection
client.lifecycle().subscribe(lifecycleEvent -> {
switch (lifecycleEvent.getType()) {
case OPENED:
Log.d(TAG, "Stomp connection opened");
break;
case CLOSED:
Log.d(TAG, "Stomp connection closed");
break;
case ERROR:
Log.e(TAG, "Stomp connection error", lifecycleEvent.getException());
break;
}
});
Custom client
You can use a custom OkHttpClient (for example, if you want to allow untrusted HTTPS) using the four-argument overload of Stomp.over, like so:
client = Stomp.over(Stomp.ConnectionProvider.OKHTTP, address, null, getUnsafeOkHttpClient());
Yes, it's safe to pass null
for either (or both) of the last two arguments. That's exactly what the shorter overloads do.
Note: This method is only supported using OkHttp, not JWS.
Heartbeating
STOMP Heartbeat implementation is in progress. Right now, you can send a heartbeat request header upon initial websocket connect:
// ask server to send us heartbeat every ten seconds
client.setHeartbeat(10000);
client.connect();
Support
Right now, the library only supports sending and receiving messages. ACK messages and transactions are not implemented yet.
Summary
Improvements: Most of the Rx logic has been rewritten, and a good portion of the other code has also been modified to allow for more stability. Additionally, a lot of blocking code has been replaced with reactive code, resulting in better performance.
Drawbacks: In order to allow for major changes, this branch sacrifices backward compatibility. Code written for the upstream will likely have to be modified to work with this version. You can find more details below.
Build changes
The upstream master is based on Retrolambda. This version is based on Native Java 8 compilation, although it should also work with Jack. It will not work with Retrolambda.
Code changes
These are the possible changes you need to make to your code for this branch, if you were using the upstream before:
- Disconnecting is now mandatory
- Previously, the socket would automatically disconnect if nothing was listening to it
- Now, it will not disconnect unless you explicitly run client.disconnect()
- send() now returns a Completable
- Previously, it returned an Observable<Void>
- Now, it and all its overloads return a Completable, which is functionally about the same
- However, Completable does not inherit from Observable, so there are a couple differences:
- Of course, if you were storing send() in an Observable, you'll have to change that
- Additionally, if you were inheriting onNext before, you're going to have to adjust it:
- Old way, deprecated:
!! -v:
client.send("/app/hello", "world").subscribe( aVoid -> Log.d(TAG, "Sent data!"), error -> Log.e(TAG, "Encountered error while sending data!", error) );
client.send("/app/hello", "world").subscribe(new Subscriber<Void>() { @Override public void onNext(Void aVoid) { Log.d(TAG, "Sent data!"); } @Override public void onError(Throwable error) { Log.e(TAG, "Encountered error while sending data!", error); } @Override public void onCompleted() {} // useless });
- New way of handling it:
Or if you just can't get enough of anonymous classes:
client.send("/app/hello", "world").subscribe( () -> Log.d(TAG, "Sent data!"), error -> Log.e(TAG, "Encountered error while sending data!", error) );
client.send("/app/hello", "world").subscribe(new Subscriber<Object /*This can be anything*/ >() { @Override public void onCompleted() { Log.d(TAG, "Sent data!"); } @Override public void onError(Throwable error) { Log.e(TAG, "Encountered error while sending data!", error); } @Override public void onNext(Object o) {} // useless });
- Old way, deprecated:
- Be sure to implement this change, because the IDE might not catch the error.
- Removed
reconnect
parameter fromconnect(...)
methods- Old, deprecated overloads of the method:
void connect(boolean reconnect) {...} void connect(List<StompHeader> _headers, boolean reconnect) {...}
- Now, these are the only two ways to call
connect
(these existed before, too):void connect() {...} void connect(List<StompHeader> _headers) {...}
- Additionally, reconnection is now handled by just calling
reconnect()
- It automatically attaches the last-used connect headers
- It is meant to be used when already connected; it executes
disconnect()
- Note that if you're already disconnected, this will throw a
TimeoutException
- Old, deprecated overloads of the method:
- Passing null as the topic path now throws an exception
- Previously, it was supposed to silently fail, although it would probably hit a NPE first (untested)
- Now it throws an IllegalArgumentException
- Connection Provider is selected by an enum
- It used to be done by passing it a WebSocket class from either JWS or OkHttp
- New way of using it can be seen in the examples above
- Topic subscription now supports wildcard parsing
- Example: RabbitMQ allows subscribing to topics with wildcards (more info)
- Usage:
StompClient client = Stomp.over(...); client.setParser(StompClient.Parser.RABBITMQ); client.connect(); client.topic("/topic/*").subscribe(message -> { Log.i(TAG, "Received message: " + message.getPayload()); });
- Rudimentary heartbeat mechanism
- You can use
StompClient.setHeartbeat(ms interval)
to send a heartbeat header to the server - WIP; currently we don't deal with those heartbeats in any way other than printing them to console
- You can use
- Better adherence to STOMP spec
- According to the spec, the end of the message body should be immediately followed by a NULL octet, marking the end of the frame.
- Before, we were adding an extra two newlines between the body and NULL octet, which was breaking compatibility with picky servers.
- Now, we format it correctly; there is no whitespace between the end of the body and the
\u0000
. - This shouldn't make any difference, but if it does, you can revert to legacy formatting with
client.setLegacyWhitespace(true)
.
- Spring + Websockets + STOMP
- STOMP Protocol
- Spring detailed documentation
- Create an Unsafe OkHttp Client
- (for developing with invalid SSL certs)