1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jdk.incubator.http.internal.websocket; 25 26 import jdk.incubator.http.WebSocket; 27 import jdk.incubator.http.WebSocket.MessagePart; 28 29 import java.nio.ByteBuffer; 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.Objects; 33 import java.util.concurrent.CompletableFuture; 34 import java.util.concurrent.CompletionStage; 35 36 import static jdk.incubator.http.internal.websocket.TestSupport.fullCopy; 37 38 public class MockListener implements WebSocket.Listener { 39 40 private final long bufferSize; 41 private long count; 42 private final List<ListenerInvocation> invocations = new ArrayList<>(); 43 private final CompletableFuture<?> lastCall = new CompletableFuture<>(); 44 45 /* 46 * Typical buffer sizes: 1, n, Long.MAX_VALUE 47 */ 48 public MockListener(long bufferSize) { 49 if (bufferSize < 1) { 50 throw new IllegalArgumentException(); 51 } 52 this.bufferSize = bufferSize; 53 } 54 55 @Override 56 public void onOpen(WebSocket webSocket) { 57 System.out.printf("onOpen(%s)%n", webSocket); 58 invocations.add(new OnOpen(webSocket)); 59 onOpen0(webSocket); 60 } 61 62 protected void onOpen0(WebSocket webSocket) { 63 replenish(webSocket); 64 } 65 66 @Override 67 public CompletionStage<?> onText(WebSocket webSocket, 68 CharSequence message, 69 MessagePart part) { 70 System.out.printf("onText(%s, %s, %s)%n", webSocket, message, part); 71 invocations.add(new OnText(webSocket, message.toString(), part)); 72 return onText0(webSocket, message, part); 73 } 74 75 protected CompletionStage<?> onText0(WebSocket webSocket, 76 CharSequence message, 77 MessagePart part) { 78 replenish(webSocket); 79 return null; 80 } 81 82 @Override 83 public CompletionStage<?> onBinary(WebSocket webSocket, 84 ByteBuffer message, 85 MessagePart part) { 86 System.out.printf("onBinary(%s, %s, %s)%n", webSocket, message, part); 87 invocations.add(new OnBinary(webSocket, fullCopy(message), part)); 88 return onBinary0(webSocket, message, part); 89 } 90 91 protected CompletionStage<?> onBinary0(WebSocket webSocket, 92 ByteBuffer message, 93 MessagePart part) { 94 replenish(webSocket); 95 return null; 96 } 97 98 @Override 99 public CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) { 100 System.out.printf("onPing(%s, %s)%n", webSocket, message); 101 invocations.add(new OnPing(webSocket, fullCopy(message))); 102 return onPing0(webSocket, message); 103 } 104 105 protected CompletionStage<?> onPing0(WebSocket webSocket, ByteBuffer message) { 106 replenish(webSocket); 107 return null; 108 } 109 110 @Override 111 public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) { 112 System.out.printf("onPong(%s, %s)%n", webSocket, message); 113 invocations.add(new OnPong(webSocket, fullCopy(message))); 114 return onPong0(webSocket, message); 115 } 116 117 protected CompletionStage<?> onPong0(WebSocket webSocket, ByteBuffer message) { 118 replenish(webSocket); 119 return null; 120 } 121 122 @Override 123 public CompletionStage<?> onClose(WebSocket webSocket, 124 int statusCode, 125 String reason) { 126 System.out.printf("onClose(%s, %s, %s)%n", webSocket, statusCode, reason); 127 invocations.add(new OnClose(webSocket, statusCode, reason)); 128 lastCall.complete(null); 129 return null; 130 } 131 132 @Override 133 public void onError(WebSocket webSocket, Throwable error) { 134 System.out.printf("onError(%s, %s)%n", webSocket, error); 135 invocations.add(new OnError(webSocket, error == null ? null : error.getClass())); 136 lastCall.complete(null); 137 } 138 139 public CompletableFuture<?> onCloseOrOnErrorCalled() { 140 return lastCall.copy(); 141 } 142 143 protected void replenish(WebSocket webSocket) { 144 if (--count <= 0) { 145 count = bufferSize - bufferSize / 2; 146 } 147 webSocket.request(count); 148 } 149 150 public List<ListenerInvocation> invocations() { 151 return new ArrayList<>(invocations); 152 } 153 154 public abstract static class ListenerInvocation { 155 156 public static OnOpen onOpen(WebSocket webSocket) { 157 return new OnOpen(webSocket); 158 } 159 160 public static OnText onText(WebSocket webSocket, 161 String text, 162 MessagePart part) { 163 return new OnText(webSocket, text, part); 164 } 165 166 public static OnBinary onBinary(WebSocket webSocket, 167 ByteBuffer data, 168 MessagePart part) { 169 return new OnBinary(webSocket, data, part); 170 } 171 172 public static OnPing onPing(WebSocket webSocket, 173 ByteBuffer data) { 174 return new OnPing(webSocket, data); 175 } 176 177 public static OnPong onPong(WebSocket webSocket, 178 ByteBuffer data) { 179 return new OnPong(webSocket, data); 180 } 181 182 public static OnClose onClose(WebSocket webSocket, 183 int statusCode, 184 String reason) { 185 return new OnClose(webSocket, statusCode, reason); 186 } 187 188 public static OnError onError(WebSocket webSocket, 189 Class<? extends Throwable> clazz) { 190 return new OnError(webSocket, clazz); 191 } 192 193 final WebSocket webSocket; 194 195 private ListenerInvocation(WebSocket webSocket) { 196 this.webSocket = webSocket; 197 } 198 } 199 200 public static final class OnOpen extends ListenerInvocation { 201 202 public OnOpen(WebSocket webSocket) { 203 super(webSocket); 204 } 205 206 @Override 207 public boolean equals(Object o) { 208 if (this == o) return true; 209 if (o == null || getClass() != o.getClass()) return false; 210 ListenerInvocation that = (ListenerInvocation) o; 211 return Objects.equals(webSocket, that.webSocket); 212 } 213 214 @Override 215 public int hashCode() { 216 return Objects.hashCode(webSocket); 217 } 218 } 219 220 public static final class OnText extends ListenerInvocation { 221 222 final String text; 223 final MessagePart part; 224 225 public OnText(WebSocket webSocket, String text, MessagePart part) { 226 super(webSocket); 227 this.text = text; 228 this.part = part; 229 } 230 231 @Override 232 public boolean equals(Object o) { 233 if (this == o) return true; 234 if (o == null || getClass() != o.getClass()) return false; 235 OnText onText = (OnText) o; 236 return Objects.equals(text, onText.text) && 237 part == onText.part && 238 Objects.equals(webSocket, onText.webSocket); 239 } 240 241 @Override 242 public int hashCode() { 243 return Objects.hash(text, part, webSocket); 244 } 245 } 246 247 public static final class OnBinary extends ListenerInvocation { 248 249 final ByteBuffer data; 250 final MessagePart part; 251 252 public OnBinary(WebSocket webSocket, ByteBuffer data, MessagePart part) { 253 super(webSocket); 254 this.data = data; 255 this.part = part; 256 } 257 258 @Override 259 public boolean equals(Object o) { 260 if (this == o) return true; 261 if (o == null || getClass() != o.getClass()) return false; 262 OnBinary onBinary = (OnBinary) o; 263 return Objects.equals(data, onBinary.data) && 264 part == onBinary.part && 265 Objects.equals(webSocket, onBinary.webSocket); 266 } 267 268 @Override 269 public int hashCode() { 270 return Objects.hash(data, part, webSocket); 271 } 272 } 273 274 public static final class OnPing extends ListenerInvocation { 275 276 final ByteBuffer data; 277 278 public OnPing(WebSocket webSocket, ByteBuffer data) { 279 super(webSocket); 280 this.data = data; 281 } 282 283 @Override 284 public boolean equals(Object o) { 285 if (this == o) return true; 286 if (o == null || getClass() != o.getClass()) return false; 287 OnPing onPing = (OnPing) o; 288 return Objects.equals(data, onPing.data) && 289 Objects.equals(webSocket, onPing.webSocket); 290 } 291 292 @Override 293 public int hashCode() { 294 return Objects.hash(data, webSocket); 295 } 296 } 297 298 public static final class OnPong extends ListenerInvocation { 299 300 final ByteBuffer data; 301 302 public OnPong(WebSocket webSocket, ByteBuffer data) { 303 super(webSocket); 304 this.data = data; 305 } 306 307 @Override 308 public boolean equals(Object o) { 309 if (this == o) return true; 310 if (o == null || getClass() != o.getClass()) return false; 311 OnPong onPong = (OnPong) o; 312 return Objects.equals(data, onPong.data) && 313 Objects.equals(webSocket, onPong.webSocket); 314 } 315 316 @Override 317 public int hashCode() { 318 return Objects.hash(data, webSocket); 319 } 320 } 321 322 public static final class OnClose extends ListenerInvocation { 323 324 final int statusCode; 325 final String reason; 326 327 public OnClose(WebSocket webSocket, int statusCode, String reason) { 328 super(webSocket); 329 this.statusCode = statusCode; 330 this.reason = reason; 331 } 332 333 @Override 334 public boolean equals(Object o) { 335 if (this == o) return true; 336 if (o == null || getClass() != o.getClass()) return false; 337 OnClose onClose = (OnClose) o; 338 return statusCode == onClose.statusCode && 339 Objects.equals(reason, onClose.reason) && 340 Objects.equals(webSocket, onClose.webSocket); 341 } 342 343 @Override 344 public int hashCode() { 345 return Objects.hash(statusCode, reason, webSocket); 346 } 347 } 348 349 public static final class OnError extends ListenerInvocation { 350 351 final Class<? extends Throwable> clazz; 352 353 public OnError(WebSocket webSocket, Class<? extends Throwable> clazz) { 354 super(webSocket); 355 this.clazz = clazz; 356 } 357 358 @Override 359 public boolean equals(Object o) { 360 if (this == o) return true; 361 if (o == null || getClass() != o.getClass()) return false; 362 OnError onError = (OnError) o; 363 return Objects.equals(clazz, onError.clazz) && 364 Objects.equals(webSocket, onError.webSocket); 365 } 366 367 @Override 368 public int hashCode() { 369 return Objects.hash(clazz, webSocket); 370 } 371 } 372 }