|
10 | 10 | import java.util.List; |
11 | 11 | import java.util.UUID; |
12 | 12 |
|
| 13 | +import java8.util.StringJoiner; |
13 | 14 | import java8.util.concurrent.CompletableFuture; |
14 | 15 | import rx.Completable; |
15 | 16 | import rx.Observable; |
@@ -39,19 +40,41 @@ public class StompClient { |
39 | 40 | private CompletableFuture<Boolean> mConnectionFuture; |
40 | 41 | private Completable mConnectionComplete; |
41 | 42 | private HashMap<String, Observable<StompMessage>> mStreamMap; |
| 43 | + private Parser parser; |
42 | 44 |
|
43 | 45 | public StompClient(ConnectionProvider connectionProvider) { |
44 | 46 | mConnectionProvider = connectionProvider; |
45 | 47 | mMessageStream = PublishSubject.create(); |
46 | 48 | mStreamMap = new HashMap<>(); |
47 | 49 | resetStatus(); |
| 50 | + parser = Parser.NONE; |
48 | 51 | } |
49 | 52 |
|
50 | 53 | private void resetStatus() { |
51 | 54 | mConnectionFuture = new CompletableFuture<>(); |
52 | 55 | mConnectionComplete = Completable.fromFuture(mConnectionFuture).subscribeOn(Schedulers.newThread()); |
53 | 56 | } |
54 | 57 |
|
| 58 | + public enum Parser { |
| 59 | + NONE, |
| 60 | + RABBITMQ |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * Set the wildcard parser for Topic subscription. |
| 65 | + * <p> |
| 66 | + * Right now, the only options are NONE and RABBITMQ. |
| 67 | + * <p> |
| 68 | + * When set to RABBITMQ, topic subscription allows for RMQ-style wildcards. |
| 69 | + * <p> |
| 70 | + * See more info <a href="https://www.rabbitmq.com/tutorials/tutorial-five-java.html">here</a>. |
| 71 | + * |
| 72 | + * @param parser Set to NONE by default |
| 73 | + */ |
| 74 | + public void setParser(Parser parser) { |
| 75 | + this.parser = parser; |
| 76 | + } |
| 77 | + |
55 | 78 | /** |
56 | 79 | * Connect without reconnect if connected |
57 | 80 | */ |
@@ -148,20 +171,70 @@ public Observable<StompMessage> topic(String destinationPath) { |
148 | 171 | return topic(destinationPath, null); |
149 | 172 | } |
150 | 173 |
|
151 | | - public Observable<StompMessage> topic(@Nullable String destPath, List<StompHeader> headerList) { |
| 174 | + public Observable<StompMessage> topic(@NonNull String destPath, List<StompHeader> headerList) { |
152 | 175 | if (destPath == null) |
153 | 176 | return Observable.error(new IllegalArgumentException("Topic path cannot be null")); |
154 | 177 | else if (!mStreamMap.containsKey(destPath)) |
155 | 178 | mStreamMap.put(destPath, |
156 | 179 | mMessageStream |
157 | | - .filter(msg -> destPath.equals(msg.findHeader(StompHeader.DESTINATION))) |
| 180 | + .filter(msg -> matches(destPath, msg)) |
158 | 181 | .doOnSubscribe(() -> subscribePath(destPath, headerList).subscribe()) |
159 | 182 | .doOnUnsubscribe(() -> unsubscribePath(destPath).subscribe()) |
160 | 183 | .share() |
161 | 184 | ); |
162 | 185 | return mStreamMap.get(destPath); |
163 | 186 | } |
164 | 187 |
|
| 188 | + private boolean matches(String path, StompMessage msg) { |
| 189 | + String dest = msg.findHeader(StompHeader.DESTINATION); |
| 190 | + if (dest == null) return false; |
| 191 | + boolean ret; |
| 192 | + |
| 193 | + switch (parser) { |
| 194 | + case NONE: |
| 195 | + ret = path.equals(dest); |
| 196 | + break; |
| 197 | + |
| 198 | + case RABBITMQ: |
| 199 | + // for example string "lorem.ipsum.*.sit": |
| 200 | + |
| 201 | + // split it up into ["lorem", "ipsum", "*", "sit"] |
| 202 | + String[] split = path.split("\\."); |
| 203 | + ArrayList<String> transformed = new ArrayList<>(); |
| 204 | + // check for wildcards and replace with corresponding regex |
| 205 | + for (String s : split) { |
| 206 | + switch (s) { |
| 207 | + case "*": |
| 208 | + transformed.add("[^.]+"); |
| 209 | + break; |
| 210 | + case "#": |
| 211 | + // TODO: make this work with zero-word |
| 212 | + // e.g. "lorem.#.dolor" should ideally match "lorem.dolor" |
| 213 | + transformed.add(".*"); |
| 214 | + break; |
| 215 | + default: |
| 216 | + transformed.add(s); |
| 217 | + break; |
| 218 | + } |
| 219 | + } |
| 220 | + // at this point, 'transformed' looks like ["lorem", "ipsum", "[^.]+", "sit"] |
| 221 | + StringJoiner sj = new StringJoiner("\\."); |
| 222 | + for (String s : transformed) |
| 223 | + sj.add(s); |
| 224 | + String join = sj.toString(); |
| 225 | + // join = "lorem\.ipsum\.[^.]+\.sit" |
| 226 | + |
| 227 | + ret = dest.matches(join); |
| 228 | + break; |
| 229 | + |
| 230 | + default: |
| 231 | + ret = false; |
| 232 | + break; |
| 233 | + } |
| 234 | + |
| 235 | + return ret; |
| 236 | + } |
| 237 | + |
165 | 238 | private Completable subscribePath(String destinationPath, @Nullable List<StompHeader> headerList) { |
166 | 239 | String topicId = UUID.randomUUID().toString(); |
167 | 240 |
|
|
0 commit comments