1 /* 2 * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 package software.amazon.eventstream; 16 17 import java.io.DataOutputStream; 18 import java.io.IOException; 19 import java.nio.ByteBuffer; 20 import java.time.Instant; 21 import java.util.Arrays; 22 import java.util.Base64; 23 import java.util.Date; 24 import java.util.UUID; 25 26 import static java.util.Objects.requireNonNull; 27 import static software.amazon.eventstream.HeaderType.TIMESTAMP; 28 import static software.amazon.eventstream.HeaderType.fromTypeId; 29 import static software.amazon.eventstream.Utils.writeBytes; 30 import static software.amazon.eventstream.Utils.writeString; 31 32 /** 33 * A typed header value. The underlying value can be obtained by calling the 34 * appropriate getter. 35 */ 36 public abstract class HeaderValue { fromBoolean(boolean value)37 public static HeaderValue fromBoolean(boolean value) { 38 return new BooleanValue(value); 39 } 40 fromByte(byte value)41 public static HeaderValue fromByte(byte value) { 42 return new ByteValue(value); 43 } 44 fromShort(short value)45 public static HeaderValue fromShort(short value) { 46 return new ShortValue(value); 47 } 48 fromInteger(int value)49 public static HeaderValue fromInteger(int value) { 50 return new IntegerValue(value); 51 } 52 fromLong(long value)53 public static HeaderValue fromLong(long value) { 54 return new LongValue(value); 55 } 56 fromByteArray(byte[] bytes)57 public static HeaderValue fromByteArray(byte[] bytes) { 58 return new ByteArrayValue(bytes); 59 } 60 fromByteBuffer(ByteBuffer buf)61 public static HeaderValue fromByteBuffer(ByteBuffer buf) { 62 buf = buf.duplicate(); 63 byte[] bytes = new byte[buf.remaining()]; 64 buf.get(bytes); 65 return fromByteArray(bytes); 66 } 67 fromString(String string)68 public static HeaderValue fromString(String string) { 69 return new StringValue(string); 70 } 71 fromTimestamp(Instant value)72 public static HeaderValue fromTimestamp(Instant value) { 73 return new TimestampValue(value); 74 } 75 fromDate(Date value)76 public static HeaderValue fromDate(Date value) { 77 return new TimestampValue(value.toInstant()); 78 } 79 fromUuid(UUID value)80 public static HeaderValue fromUuid(UUID value) { 81 return new UuidValue(value); 82 } 83 HeaderValue()84 protected HeaderValue() {} 85 getType()86 public abstract HeaderType getType(); 87 getBoolean()88 public boolean getBoolean() { 89 throw new IllegalStateException(); 90 } 91 getByte()92 public byte getByte() { 93 throw new IllegalStateException("Expected byte, but type was " + getType().name()); 94 } 95 getShort()96 public short getShort() { 97 throw new IllegalStateException("Expected short, but type was " + getType().name()); 98 } 99 getInteger()100 public int getInteger() { 101 throw new IllegalStateException("Expected integer, but type was " + getType().name()); 102 } 103 getLong()104 public long getLong() { 105 throw new IllegalStateException("Expected long, but type was " + getType().name()); 106 } 107 getByteArray()108 public byte[] getByteArray() { 109 throw new IllegalStateException(); 110 } 111 getByteBuffer()112 public final ByteBuffer getByteBuffer() { 113 return ByteBuffer.wrap(getByteArray()); 114 } 115 getString()116 public String getString() { 117 throw new IllegalStateException(); 118 } 119 getTimestamp()120 public Instant getTimestamp() { 121 throw new IllegalStateException("Expected timestamp, but type was " + getType().name()); 122 } 123 getDate()124 public Date getDate() { 125 return Date.from(getTimestamp()); 126 } 127 getUuid()128 public UUID getUuid() { 129 throw new IllegalStateException("Expected UUID, but type was " + getType().name()); 130 } 131 encode(DataOutputStream dos)132 void encode(DataOutputStream dos) throws IOException { 133 dos.writeByte(getType().headerTypeId); 134 encodeValue(dos); 135 } 136 encodeValue(DataOutputStream dos)137 abstract void encodeValue(DataOutputStream dos) throws IOException; 138 decode(ByteBuffer buf)139 static HeaderValue decode(ByteBuffer buf) { 140 HeaderType type = fromTypeId(buf.get()); 141 switch (type) { 142 case TRUE: 143 return new BooleanValue(true); 144 case FALSE: 145 return new BooleanValue(false); 146 case BYTE: 147 return new ByteValue(buf.get()); 148 case SHORT: 149 return new ShortValue(buf.getShort()); 150 case INTEGER: 151 return fromInteger(buf.getInt()); 152 case LONG: 153 return new LongValue(buf.getLong()); 154 case BYTE_ARRAY: 155 return fromByteArray(Utils.readBytes(buf)); 156 case STRING: 157 return fromString(Utils.readString(buf)); 158 case TIMESTAMP: 159 return TimestampValue.decode(buf); 160 case UUID: 161 return UuidValue.decode(buf); 162 default: 163 throw new IllegalStateException(); 164 } 165 } 166 167 private static final class BooleanValue extends HeaderValue { 168 private final boolean value; 169 BooleanValue(boolean value)170 private BooleanValue(boolean value) { 171 this.value = value; 172 } 173 174 @Override getType()175 public HeaderType getType() { 176 if (value) { 177 return HeaderType.TRUE; 178 } else { 179 return HeaderType.FALSE; 180 } 181 } 182 183 @Override getBoolean()184 public boolean getBoolean() { 185 return value; 186 } 187 188 @Override encodeValue(DataOutputStream dos)189 void encodeValue(DataOutputStream dos) {} 190 191 @Override equals(Object o)192 public boolean equals(Object o) { 193 if (this == o) return true; 194 if (o == null || getClass() != o.getClass()) return false; 195 196 BooleanValue that = (BooleanValue) o; 197 198 return value == that.value; 199 } 200 201 @Override hashCode()202 public int hashCode() { 203 if (value) { 204 return 1; 205 } else { 206 return 0; 207 } 208 } 209 210 @Override toString()211 public String toString() { 212 return String.valueOf(value); 213 } 214 } 215 216 private static final class ByteValue extends HeaderValue { 217 private final byte value; 218 ByteValue(byte value)219 private ByteValue(byte value) { 220 this.value = value; 221 } 222 223 @Override getType()224 public HeaderType getType() { 225 return HeaderType.BYTE; 226 } 227 228 @Override getByte()229 public byte getByte() { 230 return value; 231 } 232 233 @Override encodeValue(DataOutputStream dos)234 void encodeValue(DataOutputStream dos) {} 235 236 @Override equals(Object o)237 public boolean equals(Object o) { 238 if (this == o) return true; 239 if (o == null || getClass() != o.getClass()) return false; 240 241 ByteValue that = (ByteValue) o; 242 243 return value == that.value; 244 } 245 246 @Override hashCode()247 public int hashCode() { 248 return (int) value; 249 } 250 251 @Override toString()252 public String toString() { 253 return String.valueOf(value); 254 } 255 } 256 257 private static final class ShortValue extends HeaderValue { 258 private final short value; 259 ShortValue(short value)260 private ShortValue(short value) { 261 this.value = value; 262 } 263 264 @Override getType()265 public HeaderType getType() { 266 return HeaderType.SHORT; 267 } 268 269 @Override getShort()270 public short getShort() { 271 return value; 272 } 273 274 @Override encodeValue(DataOutputStream dos)275 void encodeValue(DataOutputStream dos) {} 276 277 @Override equals(Object o)278 public boolean equals(Object o) { 279 if (this == o) return true; 280 if (o == null || getClass() != o.getClass()) return false; 281 282 ShortValue that = (ShortValue) o; 283 284 return value == that.value; 285 } 286 287 @Override hashCode()288 public int hashCode() { 289 return (int) value; 290 } 291 292 @Override toString()293 public String toString() { 294 return String.valueOf(value); 295 } 296 } 297 298 private static final class IntegerValue extends HeaderValue { 299 private final int value; 300 IntegerValue(int value)301 private IntegerValue(int value) { 302 this.value = value; 303 } 304 305 @Override getType()306 public HeaderType getType() { 307 return HeaderType.INTEGER; 308 } 309 310 @Override getInteger()311 public int getInteger() { 312 return value; 313 } 314 315 @Override encodeValue(DataOutputStream dos)316 void encodeValue(DataOutputStream dos) throws IOException { 317 dos.writeInt(value); 318 } 319 320 @Override equals(Object o)321 public boolean equals(Object o) { 322 if (this == o) return true; 323 if (o == null || getClass() != o.getClass()) return false; 324 325 IntegerValue that = (IntegerValue) o; 326 327 return value == that.value; 328 } 329 330 @Override hashCode()331 public int hashCode() { 332 return value; 333 } 334 335 @Override toString()336 public String toString() { 337 return String.valueOf(value); 338 } 339 } 340 341 private static final class LongValue extends HeaderValue { 342 private final long value; 343 LongValue(long value)344 private LongValue(long value) { 345 this.value = value; 346 } 347 348 @Override getType()349 public HeaderType getType() { 350 return HeaderType.LONG; 351 } 352 353 @Override getLong()354 public long getLong() { 355 return value; 356 } 357 358 @Override encodeValue(DataOutputStream dos)359 void encodeValue(DataOutputStream dos) throws IOException { 360 dos.writeLong(value); 361 } 362 363 @Override equals(Object o)364 public boolean equals(Object o) { 365 if (this == o) return true; 366 if (o == null || getClass() != o.getClass()) return false; 367 368 LongValue longValue = (LongValue) o; 369 370 return value == longValue.value; 371 } 372 373 @Override hashCode()374 public int hashCode() { 375 return (int) (value ^ (value >>> 32)); 376 } 377 378 @Override toString()379 public String toString() { 380 return String.valueOf(value); 381 } 382 } 383 384 private static final class ByteArrayValue extends HeaderValue { 385 private final byte[] value; 386 ByteArrayValue(byte[] value)387 private ByteArrayValue(byte[] value) { 388 this.value = requireNonNull(value); 389 } 390 391 @Override getType()392 public HeaderType getType() { 393 return HeaderType.BYTE_ARRAY; 394 } 395 396 @Override getByteArray()397 public byte[] getByteArray() { 398 return value; 399 } 400 401 @Override encodeValue(DataOutputStream dos)402 void encodeValue(DataOutputStream dos) throws IOException { 403 writeBytes(dos, value); 404 } 405 406 @Override equals(Object o)407 public boolean equals(Object o) { 408 if (this == o) return true; 409 if (o == null || getClass() != o.getClass()) return false; 410 411 ByteArrayValue that = (ByteArrayValue) o; 412 413 return Arrays.equals(value, that.value); 414 } 415 416 @Override hashCode()417 public int hashCode() { 418 return Arrays.hashCode(value); 419 } 420 421 @Override toString()422 public String toString() { 423 return Base64.getEncoder().encodeToString(value); 424 } 425 } 426 427 private static final class StringValue extends HeaderValue { 428 private final String value; 429 StringValue(String value)430 private StringValue(String value) { 431 this.value = requireNonNull(value); 432 } 433 434 @Override getType()435 public HeaderType getType() { 436 return HeaderType.STRING; 437 } 438 439 @Override getString()440 public String getString() { 441 return value; 442 } 443 444 @Override encodeValue(DataOutputStream dos)445 void encodeValue(DataOutputStream dos) throws IOException { 446 writeString(dos, value); 447 } 448 449 @Override equals(Object o)450 public boolean equals(Object o) { 451 if (this == o) return true; 452 if (o == null || getClass() != o.getClass()) return false; 453 454 StringValue that = (StringValue) o; 455 456 return value.equals(that.value); 457 } 458 459 @Override hashCode()460 public int hashCode() { 461 return value.hashCode(); 462 } 463 464 @Override toString()465 public String toString() { 466 return '"' + value + '"'; 467 } 468 } 469 470 private static final class TimestampValue extends HeaderValue { 471 private final Instant value; 472 TimestampValue(Instant value)473 private TimestampValue(Instant value) { 474 this.value = requireNonNull(value); 475 } 476 decode(ByteBuffer buf)477 static TimestampValue decode(ByteBuffer buf) { 478 long epochMillis = buf.getLong(); 479 return new TimestampValue(Instant.ofEpochMilli(epochMillis)); 480 } 481 482 @Override getType()483 public HeaderType getType() { 484 return TIMESTAMP; 485 } 486 487 @Override getTimestamp()488 public Instant getTimestamp() { 489 return value; 490 } 491 492 @Override encodeValue(DataOutputStream dos)493 void encodeValue(DataOutputStream dos) throws IOException { 494 dos.writeLong(value.toEpochMilli()); 495 } 496 497 @Override equals(Object o)498 public boolean equals(Object o) { 499 if (this == o) return true; 500 if (o == null || getClass() != o.getClass()) return false; 501 502 TimestampValue that = (TimestampValue) o; 503 504 return value.equals(that.value); 505 } 506 507 @Override hashCode()508 public int hashCode() { 509 return value.hashCode(); 510 } 511 512 @Override toString()513 public String toString() { 514 return value.toString(); 515 } 516 } 517 518 private static final class UuidValue extends HeaderValue { 519 private final UUID value; 520 UuidValue(UUID value)521 private UuidValue(UUID value) { 522 this.value = requireNonNull(value); 523 } 524 decode(ByteBuffer buf)525 static UuidValue decode(ByteBuffer buf) { 526 long msb = buf.getLong(); 527 long lsb = buf.getLong(); 528 return new UuidValue(new UUID(msb, lsb)); 529 } 530 531 @Override getType()532 public HeaderType getType() { 533 return HeaderType.UUID; 534 } 535 536 @Override getUuid()537 public UUID getUuid() { 538 return value; 539 } 540 541 @Override encodeValue(DataOutputStream dos)542 void encodeValue(DataOutputStream dos) throws IOException { 543 dos.writeLong(value.getMostSignificantBits()); 544 dos.writeLong(value.getLeastSignificantBits()); 545 } 546 547 @Override equals(Object o)548 public boolean equals(Object o) { 549 if (this == o) return true; 550 if (o == null || getClass() != o.getClass()) return false; 551 552 UuidValue uuidValue = (UuidValue) o; 553 554 return value.equals(uuidValue.value); 555 } 556 557 @Override hashCode()558 public int hashCode() { 559 return value.hashCode(); 560 } 561 562 @Override toString()563 public String toString() { 564 return value.toString(); 565 } 566 } 567 } 568