Skip to content

Commit cbe0cfa

Browse files
committed
Merge branch 'flolom-feature/okhttp-support'
2 parents 2045f63 + 631a4e3 commit cbe0cfa

File tree

5 files changed

+229
-10
lines changed

5 files changed

+229
-10
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ Check out the full example server https://github.com/NaikSoftware/stomp-protocol
6565

6666
**Basic usage**
6767
``` java
68+
import org.java_websocket.WebSocket;
69+
6870
private StompClient mStompClient;
6971

7072
// ...
@@ -89,7 +91,8 @@ See the full example https://github.com/NaikSoftware/StompProtocolAndroid/tree/m
8991
Method `Stomp.over` consume class for create connection as first parameter.
9092
You must provide dependency for lib and pass class.
9193
At now supported connection providers:
92-
- WebSocket.class ('org.java-websocket:Java-WebSocket:1.3.0')
94+
- `org.java_websocket.WebSocket.class` ('org.java-websocket:Java-WebSocket:1.3.0')
95+
- `okhttp3.WebSocket.class` ('com.squareup.okhttp3:okhttp:3.8.0')
9396

9497
You can add own connection provider. Just implement interface `ConnectionProvider`.
9598
If you implement new provider, please create pull request :)

lib/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ dependencies {
3535
compile 'io.reactivex:rxjava:1.2.0'
3636
// Supported transports
3737
provided "org.java-websocket:java-websocket:1.3.2"
38+
provided 'com.squareup.okhttp3:okhttp:3.8.0'
3839
}
3940

4041
task sourcesJar(type: Jar) {
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package ua.naiksoftware.stomp;
2+
3+
import android.util.Log;
4+
5+
import java.util.ArrayList;
6+
import java.util.HashMap;
7+
import java.util.Iterator;
8+
import java.util.List;
9+
import java.util.Map;
10+
import java.util.TreeMap;
11+
12+
import okhttp3.Headers;
13+
import okhttp3.OkHttpClient;
14+
import okhttp3.Request;
15+
import okhttp3.Response;
16+
import okhttp3.WebSocket;
17+
import okhttp3.WebSocketListener;
18+
import okio.ByteString;
19+
import rx.Observable;
20+
import rx.Subscriber;
21+
22+
/* package */ class OkHttpConnectionProvider implements ConnectionProvider {
23+
24+
private static final String TAG = WebSocketsConnectionProvider.class.getSimpleName();
25+
26+
private final String mUri;
27+
private final Map<String, String> mConnectHttpHeaders;
28+
private final OkHttpClient mOkHttpClient;
29+
30+
private final List<Subscriber<? super LifecycleEvent>> mLifecycleSubscribers;
31+
private final List<Subscriber<? super String>> mMessagesSubscribers;
32+
33+
private WebSocket openedSocked;
34+
35+
36+
/* package */ OkHttpConnectionProvider(String uri, Map<String, String> connectHttpHeaders, OkHttpClient okHttpClient) {
37+
mUri = uri;
38+
mConnectHttpHeaders = connectHttpHeaders != null ? connectHttpHeaders : new HashMap<>();
39+
mLifecycleSubscribers = new ArrayList<>();
40+
mMessagesSubscribers = new ArrayList<>();
41+
mOkHttpClient = okHttpClient;
42+
}
43+
44+
@Override
45+
public Observable<String> messages() {
46+
Observable<String> observable = Observable.<String>create(subscriber -> {
47+
mMessagesSubscribers.add(subscriber);
48+
49+
}).doOnUnsubscribe(() -> {
50+
Iterator<Subscriber<? super String>> iterator = mMessagesSubscribers.iterator();
51+
while (iterator.hasNext()) {
52+
if (iterator.next().isUnsubscribed()) iterator.remove();
53+
}
54+
55+
if (mMessagesSubscribers.size() < 1) {
56+
Log.d(TAG, "Close web socket connection now in thread " + Thread.currentThread());
57+
openedSocked.close(1000, "");
58+
openedSocked = null;
59+
}
60+
});
61+
62+
createWebSocketConnection();
63+
return observable;
64+
}
65+
66+
private void createWebSocketConnection() {
67+
68+
if (openedSocked != null) {
69+
throw new IllegalStateException("Already have connection to web socket");
70+
}
71+
72+
Request.Builder requestBuilder = new Request.Builder()
73+
.url(mUri);
74+
75+
addConnectionHeadersToBuilder(requestBuilder, mConnectHttpHeaders);
76+
77+
openedSocked = mOkHttpClient.newWebSocket(requestBuilder.build(),
78+
new WebSocketListener() {
79+
@Override
80+
public void onOpen(WebSocket webSocket, Response response) {
81+
LifecycleEvent openEvent = new LifecycleEvent(LifecycleEvent.Type.OPENED);
82+
83+
TreeMap<String, String> headersAsMap = headersAsMap(response);
84+
85+
openEvent.setHandshakeResponseHeaders(headersAsMap);
86+
emitLifecycleEvent(openEvent);
87+
}
88+
89+
@Override
90+
public void onMessage(WebSocket webSocket, String text) {
91+
emitMessage(text);
92+
}
93+
94+
@Override
95+
public void onMessage(WebSocket webSocket, ByteString bytes) {
96+
emitMessage(bytes.utf8());
97+
}
98+
99+
@Override
100+
public void onClosed(WebSocket webSocket, int code, String reason) {
101+
emitLifecycleEvent(new LifecycleEvent(LifecycleEvent.Type.CLOSED));
102+
openedSocked = null;
103+
}
104+
105+
@Override
106+
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
107+
emitLifecycleEvent(new LifecycleEvent(LifecycleEvent.Type.ERROR, new Exception(t)));
108+
}
109+
}
110+
111+
);
112+
}
113+
114+
@Override
115+
public Observable<Void> send(String stompMessage) {
116+
return Observable.create(subscriber -> {
117+
if (openedSocked == null) {
118+
subscriber.onError(new IllegalStateException("Not connected yet"));
119+
} else {
120+
Log.d(TAG, "Send STOMP message: " + stompMessage);
121+
openedSocked.send(stompMessage);
122+
subscriber.onCompleted();
123+
}
124+
});
125+
}
126+
127+
@Override
128+
public Observable<LifecycleEvent> getLifecycleReceiver() {
129+
return Observable.<LifecycleEvent>create(subscriber -> {
130+
mLifecycleSubscribers.add(subscriber);
131+
132+
}).doOnUnsubscribe(() -> {
133+
Iterator<Subscriber<? super LifecycleEvent>> iterator = mLifecycleSubscribers.iterator();
134+
while (iterator.hasNext()) {
135+
if (iterator.next().isUnsubscribed()) iterator.remove();
136+
}
137+
});
138+
}
139+
140+
private TreeMap<String, String> headersAsMap(Response response) {
141+
TreeMap<String, String> headersAsMap = new TreeMap<>();
142+
Headers headers = response.headers();
143+
for (String key : headers.names()) {
144+
headersAsMap.put(key, headers.get(key));
145+
}
146+
return headersAsMap;
147+
}
148+
149+
private void addConnectionHeadersToBuilder(Request.Builder requestBuilder, Map<String, String> mConnectHttpHeaders) {
150+
for (Map.Entry<String, String> headerEntry : mConnectHttpHeaders.entrySet()) {
151+
requestBuilder.addHeader(headerEntry.getKey(), headerEntry.getValue());
152+
}
153+
}
154+
155+
private void emitLifecycleEvent(LifecycleEvent lifecycleEvent) {
156+
Log.d(TAG, "Emit lifecycle event: " + lifecycleEvent.getType().name());
157+
for (Subscriber<? super LifecycleEvent> subscriber : mLifecycleSubscribers) {
158+
subscriber.onNext(lifecycleEvent);
159+
}
160+
}
161+
162+
private void emitMessage(String stompMessage) {
163+
Log.d(TAG, "Emit STOMP message: " + stompMessage);
164+
for (Subscriber<? super String> subscriber : mMessagesSubscribers) {
165+
subscriber.onNext(stompMessage);
166+
}
167+
}
168+
}

lib/src/main/java/ua/naiksoftware/stomp/Stomp.java

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
import java.util.Map;
66

7+
import okhttp3.OkHttpClient;
78
import ua.naiksoftware.stomp.client.StompClient;
89

910
/**
1011
* Supported overlays:
1112
* - org.java_websocket.WebSocket ('org.java-websocket:Java-WebSocket:1.3.0')
13+
* - okhttp3.WebSocket ('com.squareup.okhttp3:okhttp:3.8.0')
1214
*
1315
* You can add own relay, just implement ConnectionProvider for you stomp transport,
1416
* such as web socket.
@@ -18,7 +20,7 @@
1820
public class Stomp {
1921

2022
public static StompClient over(Class clazz, String uri) {
21-
return over(clazz, uri, null);
23+
return over(clazz, uri, null, null);
2224
}
2325

2426
/**
@@ -29,14 +31,58 @@ public static StompClient over(Class clazz, String uri) {
2931
* @return StompClient for receiving and sending messages. Call #StompClient.connect
3032
*/
3133
public static StompClient over(Class clazz, String uri, Map<String, String> connectHttpHeaders) {
32-
if (clazz == WebSocket.class) {
33-
return createStompClient(new WebSocketsConnectionProvider(uri, connectHttpHeaders));
34-
}
34+
return over(clazz, uri, connectHttpHeaders, null);
35+
}
36+
37+
/**
38+
* {@code webSocketClient} can accept the following type of clients:
39+
* <ul>
40+
* <li>{@code org.java_websocket.WebSocket}: cannot accept an existing client</li>
41+
* <li>{@code okhttp3.WebSocket}: can accept a non-null instance of {@code okhttp3.OkHttpClient}</li>
42+
* </ul>
43+
* @param clazz class for using as transport
44+
* @param uri URI to connect
45+
* @param connectHttpHeaders HTTP headers, will be passed with handshake query, may be null
46+
* @param webSocketClient Existing client that will be used to open the WebSocket connection, may be null to use default client
47+
* @return StompClient for receiving and sending messages. Call #StompClient.connect
48+
*/
49+
public static StompClient over(Class clazz, String uri, Map<String, String> connectHttpHeaders, Object webSocketClient) {
50+
try {
51+
if (Class.forName("org.java_websocket.WebSocket") != null && clazz == WebSocket.class) {
52+
53+
if (webSocketClient != null) {
54+
throw new IllegalArgumentException("You cannot pass a webSocketClient with 'org.java_websocket.WebSocket'. use null instead.");
55+
}
56+
57+
return createStompClient(new WebSocketsConnectionProvider(uri, connectHttpHeaders));
58+
}
59+
} catch (ClassNotFoundException e) {}
60+
try {
61+
if (Class.forName("okhttp3.WebSocket") != null && clazz == okhttp3.WebSocket.class) {
62+
63+
OkHttpClient okHttpClient = getOkHttpClient(webSocketClient);
64+
65+
return createStompClient(new OkHttpConnectionProvider(uri, connectHttpHeaders, okHttpClient));
66+
}
67+
} catch (ClassNotFoundException e) {}
3568

3669
throw new RuntimeException("Not supported overlay transport: " + clazz.getName());
3770
}
3871

3972
private static StompClient createStompClient(ConnectionProvider connectionProvider) {
4073
return new StompClient(connectionProvider);
4174
}
75+
76+
private static OkHttpClient getOkHttpClient(Object webSocketClient) {
77+
if (webSocketClient != null) {
78+
if (webSocketClient instanceof OkHttpClient) {
79+
return (OkHttpClient) webSocketClient;
80+
} else {
81+
throw new IllegalArgumentException("You must pass a non-null instance of an 'okhttp3.OkHttpClient'. Or pass null to use a default websocket client.");
82+
}
83+
} else {
84+
// default http client
85+
return new OkHttpClient();
86+
}
87+
}
4288
}

lib/src/main/java/ua/naiksoftware/stomp/WebSocketsConnectionProvider.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package ua.naiksoftware.stomp;
22

3-
import android.os.Looper;
43
import android.util.Log;
54

65
import org.java_websocket.WebSocket;
@@ -27,23 +26,25 @@
2726
/**
2827
* Created by naik on 05.05.16.
2928
*/
30-
public class WebSocketsConnectionProvider implements ConnectionProvider {
29+
/* package */ class WebSocketsConnectionProvider implements ConnectionProvider {
3130

3231
private static final String TAG = WebSocketsConnectionProvider.class.getSimpleName();
3332

3433
private final String mUri;
3534
private final Map<String, String> mConnectHttpHeaders;
35+
36+
private final List<Subscriber<? super LifecycleEvent>> mLifecycleSubscribers;
37+
private final List<Subscriber<? super String>> mMessagesSubscribers;
38+
3639
private WebSocketClient mWebSocketClient;
37-
private List<Subscriber<? super LifecycleEvent>> mLifecycleSubscribers;
38-
private List<Subscriber<? super String>> mMessagesSubscribers;
3940
private boolean haveConnection;
4041
private TreeMap<String, String> mServerHandshakeHeaders;
4142

4243
/**
4344
* Support UIR scheme ws://host:port/path
4445
* @param connectHttpHeaders may be null
4546
*/
46-
public WebSocketsConnectionProvider(String uri, Map<String, String> connectHttpHeaders) {
47+
/* package */ WebSocketsConnectionProvider(String uri, Map<String, String> connectHttpHeaders) {
4748
mUri = uri;
4849
mConnectHttpHeaders = connectHttpHeaders != null ? connectHttpHeaders : new HashMap<>();
4950
mLifecycleSubscribers = new ArrayList<>();

0 commit comments

Comments
 (0)