From c872bf0223d55a7271ac98152e6b4e064361736a Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Mon, 20 Feb 2017 19:08:02 +0000 Subject: [PATCH 001/731] Update frame header to 1.0 spec (#233) * Update SETUP frame to 1.0 spec Makes frame and metadata length 24 bits. Metadata length also excludes the length field itself Moves streamId to front Makes frame type 6 bits and flags 10 bits Make all used flag 10 bits Some new unit tests to verify all this. --- .../frame/FrameHeaderFlyweight.java | 164 +++++++++++------- .../frame/RequestFrameFlyweight.java | 5 +- .../frame/SetupFrameFlyweight.java | 5 +- .../frame/FrameHeaderFlyweightTest.java | 102 +++++++++++ .../frame/SetupFrameFlyweightTest.java | 30 ++++ .../tcp/ReactiveSocketLengthCodec.java | 6 +- 6 files changed, 247 insertions(+), 65 deletions(-) create mode 100644 reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java create mode 100644 reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java index 49e1f3052..43f0044ba 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java @@ -43,33 +43,39 @@ private FrameHeaderFlyweight() {} private static final boolean INCLUDE_FRAME_LENGTH = true; + private static final int FRAME_TYPE_BITS = 6; + private static final int FRAME_TYPE_SHIFT = 16 - FRAME_TYPE_BITS; + private static final int FRAME_FLAGS_MASK = 0b0000_0011_1111_1111; + + public static final int FRAME_LENGTH_SIZE = 3; + public static final int FRAME_LENGTH_MASK = 0xFFFFFF; + private static final int FRAME_LENGTH_FIELD_OFFSET; - private static final int TYPE_FIELD_OFFSET; - private static final int FLAGS_FIELD_OFFSET; + private static final int FRAME_TYPE_AND_FLAGS_FIELD_OFFSET; private static final int STREAM_ID_FIELD_OFFSET; private static final int PAYLOAD_OFFSET; - public static final int FLAGS_I = 0b1000_0000_0000_0000; - public static final int FLAGS_M = 0b0100_0000_0000_0000; + public static final int FLAGS_I = 0b10_0000_0000; + public static final int FLAGS_M = 0b01_0000_0000; - public static final int FLAGS_KEEPALIVE_R = 0b0010_0000_0000_0000; + // TODO(lexs): These are frame specific and should not live here + public static final int FLAGS_KEEPALIVE_R = 0b00_1000_0000; - public static final int FLAGS_RESPONSE_F = 0b0010_0000_0000_0000; - public static final int FLAGS_RESPONSE_C = 0b0001_0000_0000_0000; + public static final int FLAGS_RESPONSE_F = 0b00_1000_0000; + public static final int FLAGS_RESPONSE_C = 0b00_0100_0000; - public static final int FLAGS_REQUEST_CHANNEL_F = 0b0010_0000_0000_0000; + public static final int FLAGS_REQUEST_CHANNEL_F = 0b00_1000_0000; static { if (INCLUDE_FRAME_LENGTH) { FRAME_LENGTH_FIELD_OFFSET = 0; } else { - FRAME_LENGTH_FIELD_OFFSET = -BitUtil.SIZE_OF_INT; + FRAME_LENGTH_FIELD_OFFSET = -FRAME_LENGTH_SIZE; } - TYPE_FIELD_OFFSET = FRAME_LENGTH_FIELD_OFFSET + BitUtil.SIZE_OF_INT; - FLAGS_FIELD_OFFSET = TYPE_FIELD_OFFSET + BitUtil.SIZE_OF_SHORT; - STREAM_ID_FIELD_OFFSET = FLAGS_FIELD_OFFSET + BitUtil.SIZE_OF_SHORT; - PAYLOAD_OFFSET = STREAM_ID_FIELD_OFFSET + BitUtil.SIZE_OF_INT; + STREAM_ID_FIELD_OFFSET = FRAME_LENGTH_FIELD_OFFSET + FRAME_LENGTH_SIZE; + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET = STREAM_ID_FIELD_OFFSET + BitUtil.SIZE_OF_INT; + PAYLOAD_OFFSET = FRAME_TYPE_AND_FLAGS_FIELD_OFFSET + BitUtil.SIZE_OF_SHORT; FRAME_HEADER_LENGTH = PAYLOAD_OFFSET; } @@ -79,39 +85,40 @@ public static int computeFrameHeaderLength(final FrameType frameType, int metada } public static int encodeFrameHeader( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, - final int frameLength, - final int flags, - final FrameType frameType, - final int streamId + final MutableDirectBuffer mutableDirectBuffer, + final int offset, + final int frameLength, + final int flags, + final FrameType frameType, + final int streamId ) { if (INCLUDE_FRAME_LENGTH) { - mutableDirectBuffer.putInt(offset + FRAME_LENGTH_FIELD_OFFSET, frameLength, ByteOrder.BIG_ENDIAN); + encodeLength(mutableDirectBuffer, offset + FRAME_LENGTH_FIELD_OFFSET, frameLength); } - mutableDirectBuffer.putShort(offset + TYPE_FIELD_OFFSET, (short) frameType.getEncodedType(), ByteOrder.BIG_ENDIAN); - mutableDirectBuffer.putShort(offset + FLAGS_FIELD_OFFSET, (short) flags, ByteOrder.BIG_ENDIAN); mutableDirectBuffer.putInt(offset + STREAM_ID_FIELD_OFFSET, streamId, ByteOrder.BIG_ENDIAN); + short typeAndFlags = (short) (frameType.getEncodedType() << FRAME_TYPE_SHIFT | (short) flags); + mutableDirectBuffer.putShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, typeAndFlags, ByteOrder.BIG_ENDIAN); return FRAME_HEADER_LENGTH; } public static int encodeMetadata( - final MutableDirectBuffer mutableDirectBuffer, - final int frameHeaderStartOffset, - final int metadataOffset, - final ByteBuffer metadata + final MutableDirectBuffer mutableDirectBuffer, + final int frameHeaderStartOffset, + final int metadataOffset, + final ByteBuffer metadata ) { int length = 0; final int metadataLength = metadata.remaining(); if (0 < metadataLength) { - int flags = mutableDirectBuffer.getShort(frameHeaderStartOffset + FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); - flags |= FLAGS_M; - mutableDirectBuffer.putShort(frameHeaderStartOffset + FLAGS_FIELD_OFFSET, (short)flags, ByteOrder.BIG_ENDIAN); - mutableDirectBuffer.putInt(metadataOffset, metadataLength + BitUtil.SIZE_OF_INT, ByteOrder.BIG_ENDIAN); - length += BitUtil.SIZE_OF_INT; + int typeAndFlags = mutableDirectBuffer.getShort(frameHeaderStartOffset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + typeAndFlags |= FLAGS_M; + mutableDirectBuffer.putShort(frameHeaderStartOffset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, (short) typeAndFlags, ByteOrder.BIG_ENDIAN); + + encodeLength(mutableDirectBuffer, metadataOffset, metadataLength); + length += FRAME_LENGTH_SIZE; mutableDirectBuffer.putBytes(metadataOffset + length, metadata, metadataLength); length += metadataLength; } @@ -120,9 +127,9 @@ public static int encodeMetadata( } public static int encodeData( - final MutableDirectBuffer mutableDirectBuffer, - final int dataOffset, - final ByteBuffer data + final MutableDirectBuffer mutableDirectBuffer, + final int dataOffset, + final ByteBuffer data ) { int length = 0; final int dataLength = data.remaining(); @@ -137,13 +144,13 @@ public static int encodeData( // only used for types simple enough that they don't have their own FrameFlyweights public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, - final int streamId, - int flags, - final FrameType frameType, - final ByteBuffer metadata, - final ByteBuffer data + final MutableDirectBuffer mutableDirectBuffer, + final int offset, + final int streamId, + int flags, + final FrameType frameType, + final ByteBuffer metadata, + final ByteBuffer data ) { final int frameLength = computeFrameHeaderLength(frameType, metadata.remaining(), data.remaining()); @@ -171,14 +178,16 @@ public static int encode( } public static int flags(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getShort(offset + FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + short typeAndFlags = directBuffer.getShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + return typeAndFlags & FRAME_FLAGS_MASK; } public static FrameType frameType(final DirectBuffer directBuffer, final int offset) { - FrameType result = FrameType.from(directBuffer.getShort(offset + TYPE_FIELD_OFFSET, ByteOrder.BIG_ENDIAN)); + int typeAndFlags = directBuffer.getShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + FrameType result = FrameType.from(typeAndFlags >> FRAME_TYPE_SHIFT); if (FrameType.RESPONSE == result) { - final int flags = flags(directBuffer, offset); + final int flags = typeAndFlags & FRAME_FLAGS_MASK; boolean complete = FLAGS_RESPONSE_C == (flags & FLAGS_RESPONSE_C); if (complete) { @@ -208,8 +217,8 @@ public static ByteBuffer sliceFrameData(final DirectBuffer directBuffer, final i } public static ByteBuffer sliceFrameMetadata(final DirectBuffer directBuffer, final int offset, final int length) { - final int metadataLength = Math.max(0, metadataFieldLength(directBuffer, offset) - BitUtil.SIZE_OF_INT); - final int metadataOffset = metadataOffset(directBuffer, offset) + BitUtil.SIZE_OF_INT; + final int metadataLength = Math.max(0, metadataLength(directBuffer, offset)); + final int metadataOffset = metadataOffset(directBuffer, offset) + FRAME_LENGTH_SIZE; ByteBuffer result = NULL_BYTEBUFFER; if (0 < metadataLength) { @@ -219,40 +228,77 @@ public static ByteBuffer sliceFrameMetadata(final DirectBuffer directBuffer, fin return result; } - private static int frameLength(final DirectBuffer directBuffer, final int offset, final int externalFrameLength) { - int frameLength = externalFrameLength; - - if (INCLUDE_FRAME_LENGTH) { - frameLength = directBuffer.getInt(offset + FRAME_LENGTH_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + static int frameLength(final DirectBuffer directBuffer, final int offset, final int externalFrameLength) { + if (!INCLUDE_FRAME_LENGTH) { + return externalFrameLength; } - return frameLength; + return decodeLength(directBuffer, offset + FRAME_LENGTH_FIELD_OFFSET); } - private static int computeMetadataLength(final int metadataPayloadLength) { - return metadataPayloadLength + (0 == metadataPayloadLength? 0 : BitUtil.SIZE_OF_INT); + private static int metadataFieldLength(final DirectBuffer directBuffer, final int offset) { + return computeMetadataLength(metadataLength(directBuffer, offset)); } - private static int metadataFieldLength(final DirectBuffer directBuffer, final int offset) { + private static int metadataLength(final DirectBuffer directBuffer, final int offset) { + return metadataLength(directBuffer, offset, metadataOffset(directBuffer, offset)); + } + + static int metadataLength(final DirectBuffer directBuffer, final int offset, final int metadataOffset) { int metadataLength = 0; - short flags = directBuffer.getShort(offset + FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + int flags = flags(directBuffer, offset); if (FLAGS_M == (FLAGS_M & flags)) { - metadataLength = directBuffer.getInt(metadataOffset(directBuffer, offset), ByteOrder.BIG_ENDIAN) & 0xFFFFFF; + metadataLength = decodeLength(directBuffer, metadataOffset); } return metadataLength; } + private static int computeMetadataLength(final int length) { + return length == 0 ? 0 : length + FRAME_LENGTH_SIZE; + } + + private static void encodeLength( + final MutableDirectBuffer mutableDirectBuffer, + final int offset, + final int length + ) { + if ((length & ~FRAME_LENGTH_MASK) != 0) { + throw new IllegalArgumentException("Length is larger than 24 bits"); + } + // Write each byte separately in reverse order, this mean we can write 1 << 23 without overflowing. + mutableDirectBuffer.putByte(offset, (byte) (length >> 16)); + mutableDirectBuffer.putByte(offset + 1, (byte) (length >> 8)); + mutableDirectBuffer.putByte(offset + 2, (byte) length); + } + + private static int decodeLength(final DirectBuffer directBuffer, final int offset) { + int length = (directBuffer.getByte(offset) & 0xFF) << 16; + length |= (directBuffer.getByte(offset + 1) & 0xFF) << 8; + length |= directBuffer.getByte(offset + 2) & 0xFF; + return length; + } + private static int dataLength(final DirectBuffer directBuffer, final int offset, final int externalLength) { + return dataLength(directBuffer, offset, externalLength, payloadOffset(directBuffer, offset)); + } + + static int dataLength( + final DirectBuffer directBuffer, + final int offset, + final int externalLength, + final int payloadOffset + ) { final int frameLength = frameLength(directBuffer, offset, externalLength); final int metadataLength = metadataFieldLength(directBuffer, offset); - return offset + frameLength - metadataLength - payloadOffset(directBuffer, offset); + return offset + frameLength - metadataLength - payloadOffset; } private static int payloadOffset(final DirectBuffer directBuffer, final int offset) { - final FrameType frameType = FrameType.from(directBuffer.getShort(offset + TYPE_FIELD_OFFSET, ByteOrder.BIG_ENDIAN)); + int typeAndFlags = directBuffer.getShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + FrameType frameType = FrameType.from(typeAndFlags >> FRAME_TYPE_SHIFT); int result = offset + PAYLOAD_OFFSET; switch (frameType) { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java index 6d40e52c2..47b7e03ce 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java @@ -27,8 +27,9 @@ public class RequestFrameFlyweight { private RequestFrameFlyweight() {} - public static final int FLAGS_REQUEST_CHANNEL_C = 0b0001_0000_0000_0000; - public static final int FLAGS_REQUEST_CHANNEL_N = 0b0000_1000_0000_0000; + public static final int FLAGS_REQUEST_CHANNEL_C = 0b00_0100_0000; + // TODO(lexs) Remove flag for initial N present + public static final int FLAGS_REQUEST_CHANNEL_N = 0b00_0010_0000; // relative to start of passed offset private static final int INITIAL_REQUEST_N_FIELD_OFFSET = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java index f926d3ca1..52101635b 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java @@ -27,8 +27,9 @@ public class SetupFrameFlyweight { private SetupFrameFlyweight() {} - public static final int FLAGS_WILL_HONOR_LEASE = 0b0010_0000; - public static final int FLAGS_STRICT_INTERPRETATION = 0b0001_0000; + // TODO(lexs) Add Resume enabled + public static final int FLAGS_WILL_HONOR_LEASE = 0b00_0100_0000; + public static final int FLAGS_STRICT_INTERPRETATION = 0b00_0010_0000; public static final byte CURRENT_VERSION = 0; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java new file mode 100644 index 000000000..1406efc6b --- /dev/null +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java @@ -0,0 +1,102 @@ +package io.reactivesocket.frame; + +import io.reactivesocket.FrameType; +import org.agrona.concurrent.UnsafeBuffer; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static io.reactivesocket.frame.FrameHeaderFlyweight.NULL_BYTEBUFFER; +import static org.junit.Assert.*; + +public class FrameHeaderFlyweightTest { + // Taken from spec + private static final int FRAME_MAX_SIZE = 16_777_215; + + private final UnsafeBuffer directBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); + + @Test + public void headerSize() { + int frameLength = 123456; + FrameHeaderFlyweight.encodeFrameHeader(directBuffer, 0, frameLength, 0, FrameType.SETUP, 0); + assertEquals(frameLength, FrameHeaderFlyweight.frameLength(directBuffer, 0, frameLength)); + } + + @Test + public void headerSizeMax() { + int frameLength = FRAME_MAX_SIZE; + FrameHeaderFlyweight.encodeFrameHeader(directBuffer, 0, frameLength, 0, FrameType.SETUP, 0); + assertEquals(frameLength, FrameHeaderFlyweight.frameLength(directBuffer, 0, frameLength)); + } + + @Test(expected = IllegalArgumentException.class) + public void headerSizeTooLarge() { + FrameHeaderFlyweight.encodeFrameHeader(directBuffer, 0, FRAME_MAX_SIZE + 1, 0, FrameType.SETUP, 0); + } + + @Test + public void frameLength() { + int length = FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.SETUP, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + assertEquals(length, 9); // 72 bits + } + + @Test + public void metadataLength() { + ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); + FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.SETUP, metadata, NULL_BYTEBUFFER); + assertEquals(4, FrameHeaderFlyweight.metadataLength(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + } + + @Test + public void dataLength() { + ByteBuffer data = ByteBuffer.wrap(new byte[]{1, 2, 3, 4, 5}); + int length = FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.SETUP, NULL_BYTEBUFFER, data); + assertEquals(5, FrameHeaderFlyweight.dataLength(directBuffer, 0, length, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + } + + @Test + public void metadataSlice() { + ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); + FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.REQUEST_RESPONSE, metadata, NULL_BYTEBUFFER); + metadata.rewind(); + + assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + } + + @Test + public void dataSlice() { + ByteBuffer data = ByteBuffer.wrap(new byte[]{1, 2, 3, 4, 5}); + FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.REQUEST_RESPONSE, NULL_BYTEBUFFER, data); + data.rewind(); + + assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + } + + @Test + public void streamId() { + int streamId = 1234; + FrameHeaderFlyweight.encode(directBuffer, 0, streamId, 0, FrameType.SETUP, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + assertEquals(streamId, FrameHeaderFlyweight.streamId(directBuffer, 0)); + } + + @Test + public void typeAndFlag() { + FrameType frameType = FrameType.FIRE_AND_FORGET; + int flags = 0b1110110111; + FrameHeaderFlyweight.encode(directBuffer, 0, 0, flags, frameType, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + + assertEquals(flags, FrameHeaderFlyweight.flags(directBuffer, 0)); + assertEquals(frameType, FrameHeaderFlyweight.frameType(directBuffer, 0)); + } + + @Test + public void typeAndFlagTruncated() { + FrameType frameType = FrameType.SETUP; + int flags = 0b11110110111; // 1 bit too many + FrameHeaderFlyweight.encode(directBuffer, 0, 0, flags, FrameType.SETUP, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + + assertNotEquals(flags, FrameHeaderFlyweight.flags(directBuffer, 0)); + assertEquals(flags & 0b0000_0011_1111_1111, FrameHeaderFlyweight.flags(directBuffer, 0)); + assertEquals(frameType, FrameHeaderFlyweight.frameType(directBuffer, 0)); + } +} \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java new file mode 100644 index 000000000..8564f94e3 --- /dev/null +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java @@ -0,0 +1,30 @@ +package io.reactivesocket.frame; + +import io.reactivesocket.FrameType; +import org.agrona.concurrent.UnsafeBuffer; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.*; + +public class SetupFrameFlyweightTest { + private final UnsafeBuffer directBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); + + @Test + public void validFrame() { + ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); + ByteBuffer data = ByteBuffer.wrap(new byte[]{5, 4, 3}); + SetupFrameFlyweight.encode(directBuffer, 0, 0, 5, 500, "metadata_type", "data_type", metadata, data); + + metadata.rewind(); + data.rewind(); + + assertEquals(FrameType.SETUP, FrameHeaderFlyweight.frameType(directBuffer, 0)); + assertEquals("metadata_type", SetupFrameFlyweight.metadataMimeType(directBuffer, 0)); + assertEquals("data_type", SetupFrameFlyweight.dataMimeType(directBuffer, 0)); + assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + } + +} \ No newline at end of file diff --git a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketLengthCodec.java b/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketLengthCodec.java index 877da8933..9af79df56 100644 --- a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketLengthCodec.java +++ b/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketLengthCodec.java @@ -17,11 +17,13 @@ package io.reactivesocket.transport.tcp; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import org.agrona.BitUtil; + +import static io.reactivesocket.frame.FrameHeaderFlyweight.FRAME_LENGTH_MASK; +import static io.reactivesocket.frame.FrameHeaderFlyweight.FRAME_LENGTH_SIZE; public class ReactiveSocketLengthCodec extends LengthFieldBasedFrameDecoder { public ReactiveSocketLengthCodec() { - super(Integer.MAX_VALUE, 0, BitUtil.SIZE_OF_INT, -1 * BitUtil.SIZE_OF_INT, 0); + super(FRAME_LENGTH_MASK, 0, FRAME_LENGTH_SIZE, -1 * FRAME_LENGTH_SIZE, 0); } } From f9286606f44f37ffa223273dfae634e4a0088df0 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Tue, 21 Feb 2017 23:31:55 +0000 Subject: [PATCH 002/731] Properly handle SETUP frames with RESUME_ENABLED (#235) --- .../main/java/io/reactivesocket/Frame.java | 4 +- .../frame/SetupFrameFlyweight.java | 82 +++++++++++++++++-- .../frame/SetupFrameFlyweightTest.java | 23 ++++++ 3 files changed, 101 insertions(+), 8 deletions(-) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java index 64770dfe3..0b98421d5 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java @@ -261,7 +261,7 @@ public static Frame from( final ByteBuffer data = payload.getData(); final Frame frame = - POOL.acquireFrame(SetupFrameFlyweight.computeFrameLength(metadataMimeType, dataMimeType, metadata.remaining(), data.remaining())); + POOL.acquireFrame(SetupFrameFlyweight.computeFrameLength(flags, metadataMimeType, dataMimeType, metadata.remaining(), data.remaining())); frame.length = SetupFrameFlyweight.encode( frame.directBuffer, frame.offset, flags, keepaliveInterval, maxLifetime, metadataMimeType, dataMimeType, metadata, data); @@ -272,7 +272,7 @@ public static int getFlags(final Frame frame) { ensureFrameType(FrameType.SETUP, frame); final int flags = FrameHeaderFlyweight.flags(frame.directBuffer, frame.offset); - return flags & (SetupFrameFlyweight.FLAGS_WILL_HONOR_LEASE | SetupFrameFlyweight.FLAGS_STRICT_INTERPRETATION); + return flags & SetupFrameFlyweight.VALID_FLAGS; } public static int version(final Frame frame) { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java index 52101635b..3d32770cd 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java @@ -27,19 +27,34 @@ public class SetupFrameFlyweight { private SetupFrameFlyweight() {} - // TODO(lexs) Add Resume enabled + public static final int FLAGS_RESUME_ENABLE = 0b00_1000_0000; public static final int FLAGS_WILL_HONOR_LEASE = 0b00_0100_0000; public static final int FLAGS_STRICT_INTERPRETATION = 0b00_0010_0000; + public static final int VALID_FLAGS = FLAGS_RESUME_ENABLE | FLAGS_WILL_HONOR_LEASE | FLAGS_STRICT_INTERPRETATION; + + // TODO(lexs) Update this 1.0 public static final byte CURRENT_VERSION = 0; // relative to start of passed offset private static final int VERSION_FIELD_OFFSET = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; private static final int KEEPALIVE_INTERVAL_FIELD_OFFSET = VERSION_FIELD_OFFSET + BitUtil.SIZE_OF_INT; private static final int MAX_LIFETIME_FIELD_OFFSET = KEEPALIVE_INTERVAL_FIELD_OFFSET + BitUtil.SIZE_OF_INT; - private static final int METADATA_MIME_TYPE_LENGTH_OFFSET = MAX_LIFETIME_FIELD_OFFSET + BitUtil.SIZE_OF_INT; + private static final int VARIABLE_DATA_OFFSET = MAX_LIFETIME_FIELD_OFFSET + BitUtil.SIZE_OF_INT; public static int computeFrameLength( + final int flags, + final String metadataMimeType, + final String dataMimeType, + final int metadataLength, + final int dataLength + ) { + return computeFrameLength(flags, 0, metadataMimeType, dataMimeType, metadataLength, dataLength); + } + + private static int computeFrameLength( + final int flags, + final int resumeTokenLength, final String metadataMimeType, final String dataMimeType, final int metadataLength, @@ -48,6 +63,11 @@ public static int computeFrameLength( int length = FrameHeaderFlyweight.computeFrameHeaderLength(FrameType.SETUP, metadataLength, dataLength); length += BitUtil.SIZE_OF_INT * 3; + + if ((flags & FLAGS_RESUME_ENABLE) != 0) { + length += BitUtil.SIZE_OF_SHORT + resumeTokenLength; + } + length += 1 + metadataMimeType.getBytes(StandardCharsets.UTF_8).length; length += 1 + dataMimeType.getBytes(StandardCharsets.UTF_8).length; @@ -65,7 +85,37 @@ public static int encode( final ByteBuffer metadata, final ByteBuffer data ) { - final int frameLength = computeFrameLength(metadataMimeType, dataMimeType, metadata.remaining(), data.remaining()); + if ((flags & FLAGS_RESUME_ENABLE) != 0) { + throw new IllegalArgumentException("RESUME_ENABLE not supported"); + } + + return encode( + mutableDirectBuffer, + offset, + flags, + keepaliveInterval, + maxLifetime, + FrameHeaderFlyweight.NULL_BYTEBUFFER, + metadataMimeType, + dataMimeType, + metadata, + data); + } + + // Only exposed for testing, other code shouldn't create frames with resumption tokens for now + static int encode( + final MutableDirectBuffer mutableDirectBuffer, + final int offset, + int flags, + final int keepaliveInterval, + final int maxLifetime, + final ByteBuffer resumeToken, + final String metadataMimeType, + final String dataMimeType, + final ByteBuffer metadata, + final ByteBuffer data + ) { + final int frameLength = computeFrameLength(flags, resumeToken.remaining(), metadataMimeType, dataMimeType, metadata.remaining(), data.remaining()); int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, FrameType.SETUP, 0); @@ -75,6 +125,14 @@ public static int encode( length += BitUtil.SIZE_OF_INT * 3; + if ((flags & FLAGS_RESUME_ENABLE) != 0) { + mutableDirectBuffer.putShort(offset + length, (short) resumeToken.remaining(), ByteOrder.BIG_ENDIAN); + length += BitUtil.SIZE_OF_SHORT; + int resumeTokenLength = resumeToken.remaining(); + mutableDirectBuffer.putBytes(offset + length, resumeToken, resumeTokenLength); + length += resumeTokenLength; + } + length += putMimeType(mutableDirectBuffer, offset + length, metadataMimeType); length += putMimeType(mutableDirectBuffer, offset + length, dataMimeType); @@ -97,12 +155,12 @@ public static int maxLifetime(final DirectBuffer directBuffer, final int offset) } public static String metadataMimeType(final DirectBuffer directBuffer, final int offset) { - final byte[] bytes = getMimeType(directBuffer, offset + METADATA_MIME_TYPE_LENGTH_OFFSET); + final byte[] bytes = getMimeType(directBuffer, offset + metadataMimetypeOffset(directBuffer, offset)); return new String(bytes, StandardCharsets.UTF_8); } public static String dataMimeType(final DirectBuffer directBuffer, final int offset) { - int fieldOffset = offset + METADATA_MIME_TYPE_LENGTH_OFFSET; + int fieldOffset = offset + metadataMimetypeOffset(directBuffer, offset); fieldOffset += 1 + directBuffer.getByte(fieldOffset); @@ -111,7 +169,7 @@ public static String dataMimeType(final DirectBuffer directBuffer, final int off } public static int payloadOffset(final DirectBuffer directBuffer, final int offset) { - int fieldOffset = offset + METADATA_MIME_TYPE_LENGTH_OFFSET; + int fieldOffset = offset + metadataMimetypeOffset(directBuffer, offset); final int metadataMimeTypeLength = directBuffer.getByte(fieldOffset); fieldOffset += 1 + metadataMimeTypeLength; @@ -122,6 +180,18 @@ public static int payloadOffset(final DirectBuffer directBuffer, final int offse return fieldOffset; } + private static int metadataMimetypeOffset(final DirectBuffer directBuffer, final int offset) { + return VARIABLE_DATA_OFFSET + resumeTokenTotalLength(directBuffer, offset); + } + + private static int resumeTokenTotalLength(final DirectBuffer directBuffer, final int offset) { + if ((FrameHeaderFlyweight.flags(directBuffer, offset) & FLAGS_RESUME_ENABLE) == 0) { + return 0; + } else { + return BitUtil.SIZE_OF_SHORT + directBuffer.getShort(offset + VARIABLE_DATA_OFFSET, ByteOrder.BIG_ENDIAN); + } + } + private static int putMimeType( final MutableDirectBuffer mutableDirectBuffer, final int fieldOffset, final String mimeType) { byte[] bytes = mimeType.getBytes(StandardCharsets.UTF_8); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java index 8564f94e3..9ce5c1732 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java @@ -27,4 +27,27 @@ public void validFrame() { assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); } + @Test(expected = IllegalArgumentException.class) + public void resumeNotSupported() { + SetupFrameFlyweight.encode(directBuffer, 0, SetupFrameFlyweight.FLAGS_RESUME_ENABLE, 5, 500, "", "", FrameHeaderFlyweight.NULL_BYTEBUFFER, FrameHeaderFlyweight.NULL_BYTEBUFFER); + } + + @Test + public void validResumeFrame() { + ByteBuffer token = ByteBuffer.wrap(new byte[]{2, 3}); + ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); + ByteBuffer data = ByteBuffer.wrap(new byte[]{5, 4, 3}); + SetupFrameFlyweight.encode(directBuffer, 0, SetupFrameFlyweight.FLAGS_RESUME_ENABLE, 5, 500, token, "metadata_type", "data_type", metadata, data); + + token.rewind(); + metadata.rewind(); + data.rewind(); + + assertEquals(FrameType.SETUP, FrameHeaderFlyweight.frameType(directBuffer, 0)); + assertEquals("metadata_type", SetupFrameFlyweight.metadataMimeType(directBuffer, 0)); + assertEquals("data_type", SetupFrameFlyweight.dataMimeType(directBuffer, 0)); + assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + assertEquals(SetupFrameFlyweight.FLAGS_RESUME_ENABLE, FrameHeaderFlyweight.flags(directBuffer, 0) & SetupFrameFlyweight.FLAGS_RESUME_ENABLE); + } } \ No newline at end of file From c5cf41698a2d17feb5cd100aa0dc85530300b55f Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Tue, 21 Feb 2017 23:34:14 +0000 Subject: [PATCH 003/731] Add last position to KEEPALIVE frame (#236) --- .../main/java/io/reactivesocket/Frame.java | 6 ++--- .../frame/KeepaliveFrameFlyweight.java | 13 ++++++++--- .../frame/KeepaliveFrameFlyweightTest.java | 22 +++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java index 0b98421d5..96415c39c 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java @@ -18,6 +18,7 @@ import io.reactivesocket.frame.ErrorFrameFlyweight; import io.reactivesocket.frame.FrameHeaderFlyweight; import io.reactivesocket.frame.FramePool; +import io.reactivesocket.frame.KeepaliveFrameFlyweight; import io.reactivesocket.frame.LeaseFrameFlyweight; import io.reactivesocket.frame.RequestFrameFlyweight; import io.reactivesocket.frame.RequestNFrameFlyweight; @@ -510,12 +511,11 @@ private Keepalive() {} public static Frame from(ByteBuffer data, boolean respond) { final Frame frame = - POOL.acquireFrame(FrameHeaderFlyweight.computeFrameHeaderLength(FrameType.KEEPALIVE, 0, data.remaining())); + POOL.acquireFrame(KeepaliveFrameFlyweight.computeFrameLength(data.remaining())); final int flags = respond ? FrameHeaderFlyweight.FLAGS_KEEPALIVE_R : 0; - frame.length = FrameHeaderFlyweight.encode( - frame.directBuffer, frame.offset, 0, flags, FrameType.KEEPALIVE, Frame.NULL_BYTEBUFFER, data); + frame.length = KeepaliveFrameFlyweight.encode(frame.directBuffer, frame.offset, flags, data); return frame; } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java index b40d406ab..bc10f13ea 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java @@ -16,6 +16,7 @@ package io.reactivesocket.frame; import io.reactivesocket.FrameType; +import org.agrona.BitUtil; import org.agrona.DirectBuffer; import org.agrona.MutableDirectBuffer; @@ -24,20 +25,26 @@ public class KeepaliveFrameFlyweight { private KeepaliveFrameFlyweight() {} - private static final int PAYLOAD_OFFSET = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; + private static final int LAST_POSITION_OFFSET = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; + private static final int PAYLOAD_OFFSET = LAST_POSITION_OFFSET + BitUtil.SIZE_OF_LONG; public static int computeFrameLength(final int dataLength) { - return FrameHeaderFlyweight.computeFrameHeaderLength(FrameType.SETUP, 0, dataLength); + return FrameHeaderFlyweight.computeFrameHeaderLength(FrameType.SETUP, 0, dataLength) + BitUtil.SIZE_OF_LONG; } public static int encode( final MutableDirectBuffer mutableDirectBuffer, final int offset, + int flags, final ByteBuffer data ) { final int frameLength = computeFrameLength(data.remaining()); - int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, 0, FrameType.KEEPALIVE, 0); + int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, FrameType.KEEPALIVE, 0); + + // We don't support resumability, last position is always zero + mutableDirectBuffer.putLong(length, 0); + length += BitUtil.SIZE_OF_LONG; length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java new file mode 100644 index 000000000..07475f7f5 --- /dev/null +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java @@ -0,0 +1,22 @@ +package io.reactivesocket.frame; + +import org.agrona.concurrent.UnsafeBuffer; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.*; + +public class KeepaliveFrameFlyweightTest { + private final UnsafeBuffer directBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); + + @Test + public void canReadData() { + ByteBuffer data = ByteBuffer.wrap(new byte[]{5, 4, 3}); + int length = KeepaliveFrameFlyweight.encode(directBuffer, 0, FrameHeaderFlyweight.FLAGS_KEEPALIVE_R, data); + data.rewind(); + + assertEquals(FrameHeaderFlyweight.FLAGS_KEEPALIVE_R, FrameHeaderFlyweight.flags(directBuffer, 0) & FrameHeaderFlyweight.FLAGS_KEEPALIVE_R); + assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, length)); + } +} \ No newline at end of file From f4a5fd94136eda3cb8a0fa529e6d6eb5c1a580ff Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Wed, 22 Feb 2017 14:21:14 +0000 Subject: [PATCH 004/731] Rename RESPONSE to PAYLOAD (#239) --- .../main/java/io/reactivesocket/Frame.java | 4 ++-- .../java/io/reactivesocket/FrameType.java | 2 +- .../reactivesocket/ServerReactiveSocket.java | 12 +++++----- .../frame/FrameHeaderFlyweight.java | 6 ++--- .../frame/PayloadFragmenter.java | 4 ++-- .../reactivesocket/internal/RemoteSender.java | 2 +- .../ClientReactiveSocketTest.java | 4 ++-- .../java/io/reactivesocket/FrameTest.java | 6 ++--- .../test/java/io/reactivesocket/TestUtil.java | 2 +- .../internal/ReassemblerTest.java | 22 +++++++++---------- .../internal/RemoteReceiverTest.java | 2 +- .../internal/RemoteSenderTest.java | 2 +- .../java/io/reactivesocket/test/TestUtil.java | 2 +- 13 files changed, 35 insertions(+), 35 deletions(-) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java index 96415c39c..7c055cb79 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java @@ -458,9 +458,9 @@ public static boolean isRequestChannelComplete(final Frame frame) { } } - public static class Response { + public static class PayloadFrame { - private Response() {} + private PayloadFrame() {} public static Frame from(int streamId, FrameType type, Payload payload) { final ByteBuffer data = payload.getData() != null ? payload.getData() : NULL_BYTEBUFFER; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/FrameType.java b/reactivesocket-core/src/main/java/io/reactivesocket/FrameType.java index 39a2ea69b..fb1000ca0 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/FrameType.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/FrameType.java @@ -35,7 +35,7 @@ public enum FrameType { REQUEST_N(0x09), CANCEL(0x0A, Flags.CAN_HAVE_METADATA), // Responder - RESPONSE(0x0B, Flags.CAN_HAVE_METADATA_AND_DATA), + PAYLOAD(0x0B, Flags.CAN_HAVE_METADATA_AND_DATA), ERROR(0x0C, Flags.CAN_HAVE_METADATA_AND_DATA), // Requester & Responder METADATA_PUSH(0x0D, Flags.CAN_HAVE_METADATA), diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index 7d2c38b3b..aec9fea8f 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -18,7 +18,7 @@ import io.reactivesocket.Frame.Lease; import io.reactivesocket.Frame.Request; -import io.reactivesocket.Frame.Response; +import io.reactivesocket.Frame.PayloadFrame; import io.reactivesocket.events.EventListener; import io.reactivesocket.events.EventListener.RequestType; import io.reactivesocket.events.EventPublishingSocket; @@ -201,7 +201,7 @@ private Publisher handleFrame(Frame frame) { return doReceive(streamId, requestSubscription(frame), RequestStream); case REQUEST_CHANNEL: return handleChannel(streamId, frame); - case RESPONSE: + case PAYLOAD: // TODO: Hook in receiving socket. return Px.empty(); case METADATA_PUSH: @@ -289,8 +289,8 @@ private Publisher handleRequestResponse(int streamId, Publisher r subscriptions.put(streamId, subscription); } }) - .map(payload -> Response - .from(streamId, FrameType.RESPONSE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_RESPONSE_C)) + .map(payload -> Frame.PayloadFrame + .from(streamId, FrameType.PAYLOAD, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_RESPONSE_C)) .doOnComplete(cleanup) .emitOnCancelOrError( // on cancel @@ -311,7 +311,7 @@ private Publisher handleRequestResponse(int streamId, Publisher r private Publisher doReceive(int streamId, Publisher response, RequestType requestType) { long now = publishSingleFrameReceiveEvents(streamId, requestType); Px resp = Px.from(response) - .map(payload -> Response.from(streamId, FrameType.RESPONSE, payload)); + .map(payload -> PayloadFrame.from(streamId, FrameType.PAYLOAD, payload)); RemoteSender sender = new RemoteSender(resp, () -> subscriptions.remove(streamId), streamId, 2); subscriptions.put(streamId, sender); return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, requestType); @@ -327,7 +327,7 @@ private Publisher handleChannel(int streamId, Frame firstFrame) { Px response = Px.from(requestChannel(eventPublishingSocket.decorateReceive(streamId, receiver, RequestChannel))) - .map(payload -> Response.from(streamId, FrameType.RESPONSE, payload)); + .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.PAYLOAD, payload)); RemoteSender sender = new RemoteSender(response, () -> removeSubscriptions(streamId), streamId, initialRequestN); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java index 43f0044ba..ac8bb0c03 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java @@ -158,11 +158,11 @@ public static int encode( switch (frameType) { case NEXT_COMPLETE: case COMPLETE: - outFrameType = FrameType.RESPONSE; + outFrameType = FrameType.PAYLOAD; flags |= FLAGS_RESPONSE_C; break; case NEXT: - outFrameType = FrameType.RESPONSE; + outFrameType = FrameType.PAYLOAD; break; default: outFrameType = frameType; @@ -186,7 +186,7 @@ public static FrameType frameType(final DirectBuffer directBuffer, final int off int typeAndFlags = directBuffer.getShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); FrameType result = FrameType.from(typeAndFlags >> FRAME_TYPE_SHIFT); - if (FrameType.RESPONSE == result) { + if (FrameType.PAYLOAD == result) { final int flags = typeAndFlags & FRAME_FLAGS_MASK; boolean complete = FLAGS_RESPONSE_C == (flags & FLAGS_RESPONSE_C); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java index bb6c35625..eeb57291d 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java @@ -101,14 +101,14 @@ public Frame next() { flags |= FrameHeaderFlyweight.FLAGS_RESPONSE_F; } - result = Frame.Response.from(streamId, FrameType.NEXT, metadataBuffer, dataBuffer, flags); + result = Frame.PayloadFrame.from(streamId, FrameType.NEXT, metadataBuffer, dataBuffer, flags); } if (Type.RESPONSE_COMPLETE == type) { if (isMoreFollowing) { flags |= FrameHeaderFlyweight.FLAGS_RESPONSE_F; } - result = Frame.Response.from(streamId, FrameType.NEXT_COMPLETE, metadataBuffer, dataBuffer, flags); + result = Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, metadataBuffer, dataBuffer, flags); } else if (Type.REQUEST_CHANNEL == type) { if (isMoreFollowing) { flags |= FrameHeaderFlyweight.FLAGS_REQUEST_CHANNEL_F; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java index 9e5f7ba39..1bf72402e 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java @@ -160,7 +160,7 @@ public void onError(Throwable t) { @Override public void onComplete() { - if (trySendTerminalFrame(Frame.Response.from(streamId, FrameType.COMPLETE), null)) { + if (trySendTerminalFrame(Frame.PayloadFrame.from(streamId, FrameType.COMPLETE), null)) { transportSubscription.safeOnComplete(); cleanup.run(); } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java index 35d981f5a..374980ed5 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java @@ -77,7 +77,7 @@ public void testHandleValidFrame() throws Throwable { response.subscribe(responseSub); int streamId = rule.getStreamIdForRequestType(REQUEST_RESPONSE); - rule.connection.addToReceivedBuffer(Frame.Response.from(streamId, NEXT_COMPLETE, PayloadImpl.EMPTY)); + rule.connection.addToReceivedBuffer(Frame.PayloadFrame.from(streamId, NEXT_COMPLETE, PayloadImpl.EMPTY)); responseSub.assertValueCount(1); responseSub.assertComplete(); @@ -122,7 +122,7 @@ public int sendRequestResponse(Publisher response) { TestSubscriber sub = TestSubscriber.create(); response.subscribe(sub); int streamId = rule.getStreamIdForRequestType(REQUEST_RESPONSE); - rule.connection.addToReceivedBuffer(Frame.Response.from(streamId, RESPONSE, PayloadImpl.EMPTY)); + rule.connection.addToReceivedBuffer(Frame.PayloadFrame.from(streamId, PAYLOAD, PayloadImpl.EMPTY)); sub.assertValueCount(1).assertNoErrors(); return streamId; } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java index 5842f3b6b..1c3651d0b 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java @@ -101,7 +101,7 @@ public void testWrapBytes() { final Payload anotherPayload = createPayload(Frame.NULL_BYTEBUFFER, anotherBuffer); Frame f = Frame.Request.from(1, FrameType.REQUEST_RESPONSE, payload, 1); - Frame f2 = Frame.Response.from(20, FrameType.COMPLETE, anotherPayload); + Frame f2 = Frame.PayloadFrame.from(20, FrameType.COMPLETE, anotherPayload); ByteBuffer b = f2.getByteBuffer(); f.wrap(b, 0); @@ -193,7 +193,7 @@ public void shouldReturnCorrectDataPlusMetadataForResponse(final int offset) final ByteBuffer requestMetadata = TestUtil.byteBufferFromUtf8String("response metadata"); final Payload payload = createPayload(requestMetadata, requestData); - Frame encodedFrame = Frame.Response.from(1, FrameType.RESPONSE, payload); + Frame encodedFrame = Frame.PayloadFrame.from(1, FrameType.PAYLOAD, payload); TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); reusableFrame.wrap(reusableMutableDirectBuffer, offset); @@ -288,7 +288,7 @@ public void shouldReturnCorrectDataWithoutMetadataForResponse(final int offset) final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("response data"); final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, requestData); - Frame encodedFrame = Frame.Response.from(1, FrameType.RESPONSE, payload); + Frame encodedFrame = Frame.PayloadFrame.from(1, FrameType.PAYLOAD, payload); TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); reusableFrame.wrap(reusableMutableDirectBuffer, offset); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java b/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java index 83d59400a..26d08c3c2 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java @@ -42,7 +42,7 @@ public ByteBuffer getMetadata() public static Frame utf8EncodedResponseFrame(final int streamId, final FrameType type, final String data) { - return Frame.Response.from(streamId, type, utf8EncodedPayload(data, null)); + return Frame.PayloadFrame.from(streamId, type, utf8EncodedPayload(data, null)); } public static Frame utf8EncodedErrorFrame(final int streamId, final String data) diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java index 64da64e87..f955d2dcb 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java @@ -40,7 +40,7 @@ public void shouldPassThroughUnfragmentedFrame() final ByteBuffer metadataBuffer = TestUtil.byteBufferFromUtf8String(metadata); final ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String(data); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadataBuffer, dataBuffer, 0)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, dataBuffer, 0)); //assertEquals(1, replaySubject.getValues().length); //assertEquals(data, TestUtil.byteToString(replaySubject.getValue().getData())); @@ -57,7 +57,7 @@ public void shouldNotPassThroughFragmentedFrameIfStillMoreFollowing() final ByteBuffer metadataBuffer = TestUtil.byteBufferFromUtf8String(metadata); final ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String(data); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadataBuffer, dataBuffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, dataBuffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); //assertEquals(0, replaySubject.getValues().length); } @@ -78,8 +78,8 @@ public void shouldReassembleTwoFramesWithFragmentedDataAndMetadata() final ByteBuffer metadata1Buffer = TestUtil.byteBufferFromUtf8String(metadata1); final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, 0)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, 0)); //assertEquals(1, replaySubject.getValues().length); //assertEquals(data, TestUtil.byteToString(replaySubject.getValue().getData())); @@ -99,8 +99,8 @@ public void shouldReassembleTwoFramesWithFragmentedData() final ByteBuffer data0Buffer = TestUtil.byteBufferFromUtf8String(data0); final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadataBuffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, Frame.NULL_BYTEBUFFER, data1Buffer, 0)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, Frame.NULL_BYTEBUFFER, data1Buffer, 0)); //assertEquals(1, replaySubject.getValues().length); //assertEquals(data, TestUtil.byteToString(replaySubject.getValue().getData())); @@ -120,8 +120,8 @@ public void shouldReassembleTwoFramesWithFragmentedMetadata() final ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String(data); final ByteBuffer metadata1Buffer = TestUtil.byteBufferFromUtf8String(metadata1); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, dataBuffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, Frame.NULL_BYTEBUFFER, 0)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, dataBuffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, Frame.NULL_BYTEBUFFER, 0)); //assertEquals(1, replaySubject.getValues().length); //assertEquals(data, TestUtil.byteToString(replaySubject.getValue().getData())); @@ -146,9 +146,9 @@ public void shouldReassembleTwoFramesWithFragmentedDataAndMetadataWithMoreThanTw final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); final ByteBuffer data2Buffer = TestUtil.byteBufferFromUtf8String(data2); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); - reassembler.onNext(Frame.Response.from(STREAM_ID, FrameType.NEXT, Frame.NULL_BYTEBUFFER, data2Buffer, 0)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, Frame.NULL_BYTEBUFFER, data2Buffer, 0)); //assertEquals(1, replaySubject.getValues().length); //assertEquals(data, TestUtil.byteToString(replaySubject.getValue().getData())); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java index 35c33a124..1f174f9f4 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java @@ -172,7 +172,7 @@ public void evaluate() throws Throwable { } public Frame newFrame(FrameType frameType) { - return Frame.Response.from(streamId, frameType); + return Frame.PayloadFrame.from(streamId, frameType); } public TestSubscriber subscribeToReceiver(int initialRequestN) { diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java index 3a6bb7650..c6907b21f 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java @@ -222,7 +222,7 @@ public void evaluate() throws Throwable { } public Frame newFrame(FrameType frameType) { - return Frame.Response.from(streamId, frameType); + return Frame.PayloadFrame.from(streamId, frameType); } public TestSubscriber subscribeToSender(int initialRequestN) { diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestUtil.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestUtil.java index 5b8fb0121..51912fc33 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestUtil.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestUtil.java @@ -43,7 +43,7 @@ public ByteBuffer getMetadata() public static Frame utf8EncodedResponseFrame(final int streamId, final FrameType type, final String data) { - return Frame.Response.from(streamId, type, utf8EncodedPayload(data, null)); + return Frame.PayloadFrame.from(streamId, type, utf8EncodedPayload(data, null)); } public static Frame utf8EncodedErrorFrame(final int streamId, final String data) From 8211bd51deb9dcc205d591db07f31734d77f76a4 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Wed, 22 Feb 2017 17:42:27 +0000 Subject: [PATCH 005/731] Remove REQUEST_SUB and change frame type identifiers (#240) Also add RESUME and RESUME_OK frame types, unused for now. --- .../reactivesocket/client/LoadBalancer.java | 16 -------- .../client/filter/BackupRequestSocket.java | 5 --- .../client/TestingReactiveSocket.java | 5 --- .../AbstractReactiveSocket.java | 5 --- .../reactivesocket/ClientReactiveSocket.java | 5 --- .../java/io/reactivesocket/FrameType.java | 22 ++++++----- .../io/reactivesocket/ReactiveSocket.java | 2 - .../reactivesocket/ServerReactiveSocket.java | 7 ---- .../reactivesocket/events/EventListener.java | 3 -- .../frame/FrameHeaderFlyweight.java | 1 - .../lease/DefaultLeaseHonoringSocket.java | 10 ----- .../lease/DisableLeaseSocket.java | 5 --- .../lease/DisabledLeaseAcceptingSocket.java | 6 --- .../util/ReactiveSocketDecorator.java | 38 +----------------- .../util/ReactiveSocketProxy.java | 12 ------ .../java/io/reactivesocket/FrameTest.java | 39 ------------------- .../lease/DefaultLeaseTest.java | 9 ----- .../lease/DisableLeaseSocketTest.java | 8 ---- .../DisabledLeaseAcceptingSocketTest.java | 8 ---- .../test/util/MockReactiveSocket.java | 6 --- .../reactivesocket/test/ClientSetupRule.java | 5 --- .../test/TestReactiveSocket.java | 5 --- .../aeron/ClientServerTest.java | 5 --- .../local/ClientServerTest.java | 6 --- .../transport/tcp/ClientServerTest.java | 6 --- 25 files changed, 14 insertions(+), 225 deletions(-) diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java index a92e9e6ad..e9d7d86dc 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java @@ -202,11 +202,6 @@ public Publisher requestResponse(Payload payload) { return subscriber -> select().requestResponse(payload).subscribe(subscriber); } - @Override - public Publisher requestSubscription(Payload payload) { - return subscriber -> select().requestSubscription(payload).subscribe(subscriber); - } - @Override public Publisher requestStream(Payload payload) { return subscriber -> select().requestStream(payload).subscribe(subscriber); @@ -714,11 +709,6 @@ public Publisher requestStream(Payload payload) { return errorPayload; } - @Override - public Publisher requestSubscription(Payload payload) { - return errorPayload; - } - @Override public Publisher requestChannel(Publisher payloads) { return errorPayload; @@ -814,12 +804,6 @@ public Publisher requestStream(Payload payload) { child.requestStream(payload).subscribe(new CountingSubscriber<>(subscriber, this)); } - @Override - public Publisher requestSubscription(Payload payload) { - return subscriber -> - child.requestSubscription(payload).subscribe(new CountingSubscriber<>(subscriber, this)); - } - @Override public Publisher fireAndForget(Payload payload) { return subscriber -> diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/BackupRequestSocket.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/BackupRequestSocket.java index 88628e229..2dde5ce50 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/BackupRequestSocket.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/BackupRequestSocket.java @@ -70,11 +70,6 @@ public Publisher requestStream(Payload payload) { return child.requestStream(payload); } - @Override - public Publisher requestSubscription(Payload payload) { - return child.requestSubscription(payload); - } - @Override public Publisher requestChannel(Publisher payloads) { return child.requestChannel(payloads); diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/TestingReactiveSocket.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/TestingReactiveSocket.java index 8473f6010..5b7da1c72 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/TestingReactiveSocket.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/TestingReactiveSocket.java @@ -86,11 +86,6 @@ public Publisher requestStream(Payload payload) { return requestResponse(payload); } - @Override - public Publisher requestSubscription(Payload payload) { - return requestResponse(payload); - } - @Override public Publisher requestChannel(Publisher inputs) { return subscriber -> diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/AbstractReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/AbstractReactiveSocket.java index b5c998682..86fd4ec88 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/AbstractReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/AbstractReactiveSocket.java @@ -45,11 +45,6 @@ public Publisher requestStream(Payload payload) { return Px.error(new UnsupportedOperationException("Request-Stream not implemented.")); } - @Override - public Publisher requestSubscription(Payload payload) { - return Px.error(new UnsupportedOperationException("Request-Subscription not implemented.")); - } - @Override public Publisher requestChannel(Publisher payloads) { return Px.error(new UnsupportedOperationException("Request-Channel not implemented.")); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java index fe7b9789d..69561f5d6 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java @@ -107,11 +107,6 @@ public Publisher requestStream(Payload payload) { return handleStreamResponse(Px.just(payload), FrameType.REQUEST_STREAM); } - @Override - public Publisher requestSubscription(Payload payload) { - return handleStreamResponse(Px.just(payload), FrameType.REQUEST_SUBSCRIPTION); - } - @Override public Publisher requestChannel(Publisher payloads) { return handleStreamResponse(Px.from(payloads), FrameType.REQUEST_CHANNEL); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/FrameType.java b/reactivesocket-core/src/main/java/io/reactivesocket/FrameType.java index fb1000ca0..318d509bf 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/FrameType.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/FrameType.java @@ -29,20 +29,22 @@ public enum FrameType { REQUEST_RESPONSE(0x04, Flags.CAN_HAVE_METADATA_AND_DATA | Flags.IS_REQUEST_TYPE), FIRE_AND_FORGET(0x05, Flags.CAN_HAVE_METADATA_AND_DATA | Flags.IS_REQUEST_TYPE), REQUEST_STREAM(0x06, Flags.CAN_HAVE_METADATA_AND_DATA | Flags.IS_REQUEST_TYPE | Flags.HAS_INITIAL_REQUEST_N), - REQUEST_SUBSCRIPTION(0x07, Flags.CAN_HAVE_METADATA_AND_DATA | Flags.IS_REQUEST_TYPE | Flags.HAS_INITIAL_REQUEST_N), - REQUEST_CHANNEL(0x08, Flags.CAN_HAVE_METADATA_AND_DATA | Flags.IS_REQUEST_TYPE | Flags.HAS_INITIAL_REQUEST_N), + REQUEST_CHANNEL(0x07, Flags.CAN_HAVE_METADATA_AND_DATA | Flags.IS_REQUEST_TYPE | Flags.HAS_INITIAL_REQUEST_N), // Requester mid-stream - REQUEST_N(0x09), - CANCEL(0x0A, Flags.CAN_HAVE_METADATA), + REQUEST_N(0x08), + CANCEL(0x09, Flags.CAN_HAVE_METADATA), // Responder - PAYLOAD(0x0B, Flags.CAN_HAVE_METADATA_AND_DATA), - ERROR(0x0C, Flags.CAN_HAVE_METADATA_AND_DATA), + PAYLOAD(0x0A, Flags.CAN_HAVE_METADATA_AND_DATA), + ERROR(0x0B, Flags.CAN_HAVE_METADATA_AND_DATA), // Requester & Responder - METADATA_PUSH(0x0D, Flags.CAN_HAVE_METADATA), + METADATA_PUSH(0x0C, Flags.CAN_HAVE_METADATA), + // Resumption frames, not yet implemented + RESUME(0x0D), + RESUME_OK(0x0E), // synthetic types from Responder for use by the rest of the machinery - NEXT(0x0E, Flags.CAN_HAVE_METADATA_AND_DATA), - COMPLETE(0x0F), - NEXT_COMPLETE(0x10, Flags.CAN_HAVE_METADATA_AND_DATA), + NEXT(0xA0, Flags.CAN_HAVE_METADATA_AND_DATA), + COMPLETE(0xB0), + NEXT_COMPLETE(0xC0, Flags.CAN_HAVE_METADATA_AND_DATA), EXT(0xFFFF, Flags.CAN_HAVE_METADATA_AND_DATA); private static class Flags { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocket.java index 029342671..b7198a70a 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocket.java @@ -50,8 +50,6 @@ public interface ReactiveSocket extends Availability { */ Publisher requestStream(Payload payload); - Publisher requestSubscription(Payload payload); - /** * Request-Channel interaction model of {@code ReactiveSocket}. * diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index aec9fea8f..459ef8978 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -116,11 +116,6 @@ public Publisher requestStream(Payload payload) { return requestHandler.requestStream(payload); } - @Override - public Publisher requestSubscription(Payload payload) { - return requestHandler.requestSubscription(payload); - } - @Override public Publisher requestChannel(Publisher payloads) { return requestHandler.requestChannel(payloads); @@ -197,8 +192,6 @@ private Publisher handleFrame(Frame frame) { return doReceive(streamId, requestStream(frame), RequestStream); case FIRE_AND_FORGET: return handleFireAndForget(streamId, fireAndForget(frame)); - case REQUEST_SUBSCRIPTION: - return doReceive(streamId, requestSubscription(frame), RequestStream); case REQUEST_CHANNEL: return handleChannel(streamId, frame); case PAYLOAD: diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java index c149af91f..0ffc09622 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java @@ -33,7 +33,6 @@ public interface EventListener { enum RequestType { RequestResponse, RequestStream, - RequestSubscription, RequestChannel, MetadataPush, FireAndForget; @@ -46,8 +45,6 @@ public static RequestType fromFrameType(FrameType frameType) { return FireAndForget; case REQUEST_STREAM: return RequestStream; - case REQUEST_SUBSCRIPTION: - return RequestSubscription; case REQUEST_CHANNEL: return RequestChannel; case METADATA_PUSH: diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java index ac8bb0c03..6deff1647 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java @@ -317,7 +317,6 @@ private static int payloadOffset(final DirectBuffer directBuffer, final int offs case REQUEST_RESPONSE: case FIRE_AND_FORGET: case REQUEST_STREAM: - case REQUEST_SUBSCRIPTION: case REQUEST_CHANNEL: result = RequestFrameFlyweight.payloadOffset(frameType, directBuffer, offset); break; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseHonoringSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseHonoringSocket.java index 19e45a589..4f466d26d 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseHonoringSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseHonoringSocket.java @@ -87,16 +87,6 @@ public Publisher requestStream(Payload payload) { }); } - @Override - public Publisher requestSubscription(Payload payload) { - return Px.defer(() -> { - if (!checkLease()) { - return rejectError(); - } - return delegate.requestSubscription(payload); - }); - } - @Override public Publisher requestChannel(Publisher payloads) { return Px.defer(() -> { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisableLeaseSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisableLeaseSocket.java index e1db1a917..9cbbf9c38 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisableLeaseSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisableLeaseSocket.java @@ -55,11 +55,6 @@ public Publisher requestStream(Payload payload) { return delegate.requestStream(payload); } - @Override - public Publisher requestSubscription(Payload payload) { - return delegate.requestSubscription(payload); - } - @Override public Publisher requestChannel(Publisher payloads) { return delegate.requestChannel(payloads); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocket.java index 4ffe5001e..2db2f81af 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocket.java @@ -50,12 +50,6 @@ public Publisher requestStream(Payload payload) { return delegate.requestStream(payload); } - @Override - public Publisher requestSubscription( - Payload payload) { - return delegate.requestSubscription(payload); - } - @Override public Publisher requestChannel( Publisher payloads) { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketDecorator.java b/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketDecorator.java index a88628af2..a6ca699ec 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketDecorator.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketDecorator.java @@ -33,7 +33,6 @@ public class ReactiveSocketDecorator { private Function> reqResp; private Function> reqStream; - private Function> reqSub; private Function, Publisher> reqChannel; private Function> fnf; private Function> metaPush; @@ -47,7 +46,6 @@ private ReactiveSocketDecorator(ReactiveSocket delegate) { this.delegate = delegate; reqResp = payload -> delegate.requestResponse(payload); reqStream = payload -> delegate.requestStream(payload); - reqSub = payload -> delegate.requestSubscription(payload); reqChannel = payload -> delegate.requestChannel(payload); fnf = payload -> delegate.fireAndForget(payload); metaPush = payload -> delegate.metadataPush(payload); @@ -73,11 +71,6 @@ public Publisher requestStream(Payload payload) { return reqStream.apply(payload); } - @Override - public Publisher requestSubscription(Payload payload) { - return reqSub.apply(payload); - } - @Override public Publisher requestChannel(Publisher payloads) { return reqChannel.apply(payloads); @@ -157,32 +150,6 @@ public ReactiveSocketDecorator requestStream(BiFunction, Publisher> responseMapper) { - reqSub = payload -> responseMapper.apply(delegate.requestSubscription(payload)); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#requestSubscription(Payload)} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. First argument here is - * the payload for request and the socket passed is the underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator requestSubscription(BiFunction> mapper) { - reqSub = payload -> mapper.apply(payload, delegate); - return this; - } - /** * Decorates underlying {@link ReactiveSocket#requestChannel(Publisher)} with the provided mapping function. * @@ -263,8 +230,8 @@ public ReactiveSocketDecorator metadataPush(BiFunction, Publisher> mapper) { requestResponse(resp -> mapper.apply(resp)).requestStream(resp -> mapper.apply(resp)) - .requestSubscription(resp -> mapper.apply(resp)) .requestChannel(resp -> mapper.apply(resp)); return this; } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketProxy.java b/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketProxy.java index cfa6f587a..8f97c3b73 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketProxy.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketProxy.java @@ -69,18 +69,6 @@ public Publisher requestStream(Payload payload) { } } - @Override - public Publisher requestSubscription(Payload payload) { - if (subscriberWrapper == null) { - return child.requestSubscription(payload); - } else { - return s -> { - Subscriber subscriber = subscriberWrapper.apply(s); - child.requestSubscription(payload).subscribe(subscriber); - }; - } - } - @Override public Publisher requestChannel(Publisher payloads) { if (subscriberWrapper == null) { diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java index 1c3651d0b..e11c6b908 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java @@ -166,25 +166,6 @@ public void shouldReturnCorrectDataPlusMetadataForRequestStream(final int offset assertEquals(128, Frame.Request.initialRequestN(reusableFrame)); } - @Test - @Theory - public void shouldReturnCorrectDataPlusMetadataForRequestSubscription(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("request data"); - final ByteBuffer requestMetadata = TestUtil.byteBufferFromUtf8String("request metadata"); - final Payload payload = createPayload(requestMetadata, requestData); - - Frame encodedFrame = Frame.Request.from(1, FrameType.REQUEST_SUBSCRIPTION, payload, 128); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("request data", TestUtil.byteToString(reusableFrame.getData())); - assertEquals("request metadata", TestUtil.byteToString(reusableFrame.getMetadata())); - assertEquals(FrameType.REQUEST_SUBSCRIPTION, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - assertEquals(128, Frame.Request.initialRequestN(reusableFrame)); - } - @Test @Theory public void shouldReturnCorrectDataPlusMetadataForResponse(final int offset) @@ -261,26 +242,6 @@ public void shouldReturnCorrectDataWithoutMetadataForRequestStream(final int off assertEquals(128, Frame.Request.initialRequestN(reusableFrame)); } - @Test - @Theory - public void shouldReturnCorrectDataWithoutMetadataForRequestSubscription(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("request data"); - final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, requestData); - - Frame encodedFrame = Frame.Request.from(1, FrameType.REQUEST_SUBSCRIPTION, payload, 128); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("request data", TestUtil.byteToString(reusableFrame.getData())); - - final ByteBuffer metadataBuffer = reusableFrame.getMetadata(); - assertEquals(0, metadataBuffer.remaining()); - assertEquals(FrameType.REQUEST_SUBSCRIPTION, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - assertEquals(128, Frame.Request.initialRequestN(reusableFrame)); - } - @Test @Theory public void shouldReturnCorrectDataWithoutMetadataForResponse(final int offset) diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseTest.java index 5b125e8d9..c81ec785c 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseTest.java @@ -67,15 +67,6 @@ public void testRequestStream() throws Exception { getMockSocket(state).assertRequestStreamCount(expectedInvocations); } - @Test - public void testRequestSubscription() throws Exception { - T state = init(); - TestSubscriber subscriber = TestSubscriber.create(); - getReactiveSocket(state).requestSubscription(PayloadImpl.EMPTY).subscribe(subscriber); - subscriber.assertError(expectedException); - getMockSocket(state).assertRequestSubscriptionCount(expectedInvocations); - } - @Test public void testRequestChannel() throws Exception { T state = init(); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisableLeaseSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisableLeaseSocketTest.java index 757aee624..adf11afbf 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisableLeaseSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisableLeaseSocketTest.java @@ -53,14 +53,6 @@ public void testRequestStream() throws Exception { socketRule.getMockSocket().assertRequestStreamCount(1); } - @Test(timeout = 10000) - public void testRequestSubscription() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - socketRule.getReactiveSocket().requestSubscription(PayloadImpl.EMPTY).subscribe(subscriber); - subscriber.assertError(UnsupportedOperationException.class); - socketRule.getMockSocket().assertRequestSubscriptionCount(1); - } - @Test(timeout = 10000) public void testRequestChannel() throws Exception { TestSubscriber subscriber = TestSubscriber.create(); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocketTest.java index 854276b4b..eaa099b3e 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocketTest.java @@ -52,14 +52,6 @@ public void testRequestStream() throws Exception { socketRule.getMockSocket().assertRequestStreamCount(1); } - @Test(timeout = 10000) - public void testRequestSubscription() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - socketRule.getReactiveSocket().requestSubscription(PayloadImpl.EMPTY).subscribe(subscriber); - subscriber.assertError(UnsupportedOperationException.class); - socketRule.getMockSocket().assertRequestSubscriptionCount(1); - } - @Test(timeout = 10000) public void testRequestChannel() throws Exception { TestSubscriber subscriber = TestSubscriber.create(); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/MockReactiveSocket.java b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/MockReactiveSocket.java index bd6e8834d..4540388d1 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/MockReactiveSocket.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/MockReactiveSocket.java @@ -64,12 +64,6 @@ public final Publisher requestStream(Payload payload) { .doOnSubscribe(s -> rStreamCount.incrementAndGet()); } - @Override - public final Publisher requestSubscription(Payload payload) { - return Px.from(delegate.requestSubscription(payload)) - .doOnSubscribe(s -> rSubCount.incrementAndGet()); - } - @Override public final Publisher requestChannel(Publisher payloads) { return Px.from(delegate.requestChannel(payloads)) diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java index 763a7c011..696957f33 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java @@ -97,11 +97,6 @@ public void testRequestResponseN(int count) { ts.assertTerminated(); } - public void testRequestSubscription() { - testStream( - socket -> socket.requestSubscription(TestUtil.utf8EncodedPayload("hello", "metadata"))); - } - public void testRequestStream() { testStream(socket -> socket.requestStream(TestUtil.utf8EncodedPayload("hello", "metadata"))); } diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java index b0974eed0..05c4ebaa1 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java @@ -34,11 +34,6 @@ public Publisher requestStream(Payload payload) { return Flowable.fromPublisher(requestResponse(payload)).repeat(10); } - @Override - public Publisher requestSubscription(Payload payload) { - return Flowable.fromPublisher(requestStream(payload)).repeat(); - } - @Override public Publisher fireAndForget(Payload payload) { return Subscriber::onComplete; diff --git a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/ClientServerTest.java b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/ClientServerTest.java index 6c802a5b4..0ba8bec90 100644 --- a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/ClientServerTest.java +++ b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/ClientServerTest.java @@ -51,9 +51,4 @@ public void testRequestResponse10_000() { public void testRequestStream() { setup.testRequestStream(); } - - @Test(timeout = 10000) - public void testRequestSubscription() throws InterruptedException { - setup.testRequestSubscription(); - } } \ No newline at end of file diff --git a/reactivesocket-transport-local/src/test/java/io/reactivesocket/local/ClientServerTest.java b/reactivesocket-transport-local/src/test/java/io/reactivesocket/local/ClientServerTest.java index 97628709a..a7e060991 100644 --- a/reactivesocket-transport-local/src/test/java/io/reactivesocket/local/ClientServerTest.java +++ b/reactivesocket-transport-local/src/test/java/io/reactivesocket/local/ClientServerTest.java @@ -57,12 +57,6 @@ public void testRequestStream() { setup.testRequestStream(); } - @Ignore("Stream/Subscription does not work as of now.") - @Test(timeout = 10000) - public void testRequestSubscription() throws InterruptedException { - setup.testRequestSubscription(); - } - private static class LocalRule extends ClientSetupRule { private static final AtomicInteger uniqueNameGenerator = new AtomicInteger(); diff --git a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/ClientServerTest.java b/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/ClientServerTest.java index f79bac370..3fd26e46a 100644 --- a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/ClientServerTest.java +++ b/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/ClientServerTest.java @@ -51,10 +51,4 @@ public void testRequestResponse10_000() { public void testRequestStream() { setup.testRequestStream(); } - - @Ignore("Fix request-subscription") - @Test(timeout = 10000) - public void testRequestSubscription() throws InterruptedException { - setup.testRequestSubscription(); - } } \ No newline at end of file From 0e621dfdd3495d2df95352691c7830d5b847bd03 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Wed, 22 Feb 2017 17:43:20 +0000 Subject: [PATCH 006/731] Update error codes to match 1.0 spec (#241) --- .../frame/ErrorFrameFlyweight.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java index 3076a1cac..a43eeb6c0 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java @@ -36,14 +36,16 @@ public class ErrorFrameFlyweight { private ErrorFrameFlyweight() {} // defined error codes - public static final int INVALID_SETUP = 0x0001; - public static final int UNSUPPORTED_SETUP = 0x0002; - public static final int REJECTED_SETUP = 0x0003; - public static final int CONNECTION_ERROR = 0x0101; - public static final int APPLICATION_ERROR = 0x0201; - public static final int REJECTED = 0x0022; - public static final int CANCEL = 0x0203; - public static final int INVALID = 0x0204; + public static final int INVALID_SETUP = 0x00000001; + public static final int UNSUPPORTED_SETUP = 0x00000002; + public static final int REJECTED_SETUP = 0x00000003; + public static final int REJECTED_RESUME = 0x00000004; + public static final int CONNECTION_ERROR = 0x00000101; + public static final int CONNECTION_CLOSE = 0x00000102; + public static final int APPLICATION_ERROR = 0x00000201; + public static final int REJECTED = 0x00000202; + public static final int CANCEL = 0x00000203; + public static final int INVALID = 0x00000204; // relative to start of passed offset private static final int ERROR_CODE_FIELD_OFFSET = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; From 91e3d8cd453fa9045002019c58f7217445b1fc24 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Wed, 22 Feb 2017 17:44:07 +0000 Subject: [PATCH 007/731] Simplify frame flags (#243) --- .../main/java/io/reactivesocket/Frame.java | 6 ++--- .../reactivesocket/ServerReactiveSocket.java | 2 +- .../frame/FrameHeaderFlyweight.java | 13 +++------- .../frame/KeepaliveFrameFlyweight.java | 2 ++ .../frame/PayloadFragmenter.java | 6 ++--- .../frame/PayloadReassembler.java | 2 +- .../frame/RequestFrameFlyweight.java | 1 - .../frame/KeepaliveFrameFlyweightTest.java | 4 +-- .../internal/FragmenterTest.java | 25 ++++++++++--------- .../internal/ReassemblerTest.java | 12 ++++----- 10 files changed, 35 insertions(+), 38 deletions(-) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java index 7c055cb79..c4e5f2718 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java @@ -454,7 +454,7 @@ public static boolean isRequestChannelComplete(final Frame frame) { ensureFrameType(FrameType.REQUEST_CHANNEL, frame); final int flags = FrameHeaderFlyweight.flags(frame.directBuffer, frame.offset); - return (flags & RequestFrameFlyweight.FLAGS_REQUEST_CHANNEL_C) == RequestFrameFlyweight.FLAGS_REQUEST_CHANNEL_C; + return (flags & FrameHeaderFlyweight.FLAGS_C) == FrameHeaderFlyweight.FLAGS_C; } } @@ -513,7 +513,7 @@ public static Frame from(ByteBuffer data, boolean respond) { final Frame frame = POOL.acquireFrame(KeepaliveFrameFlyweight.computeFrameLength(data.remaining())); - final int flags = respond ? FrameHeaderFlyweight.FLAGS_KEEPALIVE_R : 0; + final int flags = respond ? KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R : 0; frame.length = KeepaliveFrameFlyweight.encode(frame.directBuffer, frame.offset, flags, data); @@ -524,7 +524,7 @@ public static boolean hasRespondFlag(final Frame frame) { ensureFrameType(FrameType.KEEPALIVE, frame); final int flags = FrameHeaderFlyweight.flags(frame.directBuffer, frame.offset); - return (flags & FrameHeaderFlyweight.FLAGS_KEEPALIVE_R) == FrameHeaderFlyweight.FLAGS_KEEPALIVE_R; + return (flags & KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R) == KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R; } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index 459ef8978..fbbedc379 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -283,7 +283,7 @@ private Publisher handleRequestResponse(int streamId, Publisher r } }) .map(payload -> Frame.PayloadFrame - .from(streamId, FrameType.PAYLOAD, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_RESPONSE_C)) + .from(streamId, FrameType.PAYLOAD, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C)) .doOnComplete(cleanup) .emitOnCancelOrError( // on cancel diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java index 6deff1647..23fc11ad3 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java @@ -58,13 +58,8 @@ private FrameHeaderFlyweight() {} public static final int FLAGS_I = 0b10_0000_0000; public static final int FLAGS_M = 0b01_0000_0000; - // TODO(lexs): These are frame specific and should not live here - public static final int FLAGS_KEEPALIVE_R = 0b00_1000_0000; - - public static final int FLAGS_RESPONSE_F = 0b00_1000_0000; - public static final int FLAGS_RESPONSE_C = 0b00_0100_0000; - - public static final int FLAGS_REQUEST_CHANNEL_F = 0b00_1000_0000; + public static final int FLAGS_F = 0b00_1000_0000; + public static final int FLAGS_C = 0b00_0100_0000; static { if (INCLUDE_FRAME_LENGTH) { @@ -159,7 +154,7 @@ public static int encode( case NEXT_COMPLETE: case COMPLETE: outFrameType = FrameType.PAYLOAD; - flags |= FLAGS_RESPONSE_C; + flags |= FLAGS_C; break; case NEXT: outFrameType = FrameType.PAYLOAD; @@ -189,7 +184,7 @@ public static FrameType frameType(final DirectBuffer directBuffer, final int off if (FrameType.PAYLOAD == result) { final int flags = typeAndFlags & FRAME_FLAGS_MASK; - boolean complete = FLAGS_RESPONSE_C == (flags & FLAGS_RESPONSE_C); + boolean complete = FLAGS_C == (flags & FLAGS_C); if (complete) { result = FrameType.NEXT_COMPLETE; } else { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java index bc10f13ea..31969cdaf 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java @@ -23,6 +23,8 @@ import java.nio.ByteBuffer; public class KeepaliveFrameFlyweight { + public static final int FLAGS_KEEPALIVE_R = 0b00_1000_0000; + private KeepaliveFrameFlyweight() {} private static final int LAST_POSITION_OFFSET = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java index eeb57291d..9cd0cdebf 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java @@ -98,20 +98,20 @@ public Frame next() { if (Type.RESPONSE == type) { if (isMoreFollowing) { - flags |= FrameHeaderFlyweight.FLAGS_RESPONSE_F; + flags |= FrameHeaderFlyweight.FLAGS_F; } result = Frame.PayloadFrame.from(streamId, FrameType.NEXT, metadataBuffer, dataBuffer, flags); } if (Type.RESPONSE_COMPLETE == type) { if (isMoreFollowing) { - flags |= FrameHeaderFlyweight.FLAGS_RESPONSE_F; + flags |= FrameHeaderFlyweight.FLAGS_F; } result = Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, metadataBuffer, dataBuffer, flags); } else if (Type.REQUEST_CHANNEL == type) { if (isMoreFollowing) { - flags |= FrameHeaderFlyweight.FLAGS_REQUEST_CHANNEL_F; + flags |= FrameHeaderFlyweight.FLAGS_F; } result = Frame.Request.from(streamId, FrameType.REQUEST_CHANNEL, metadataBuffer, dataBuffer, initialRequestN, flags); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java index 4f26cdae2..e1849913f 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java @@ -47,7 +47,7 @@ public void onNext(Frame frame) { final int streamId = frame.getStreamId(); PayloadBuilder payloadBuilder = payloadByStreamId.get(streamId); - if (FrameHeaderFlyweight.FLAGS_RESPONSE_F != (frame.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)) { + if (FrameHeaderFlyweight.FLAGS_F != (frame.flags() & FrameHeaderFlyweight.FLAGS_F)) { Payload deliveryPayload = frame; // terminal frame diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java index 47b7e03ce..327c2dcf9 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java @@ -27,7 +27,6 @@ public class RequestFrameFlyweight { private RequestFrameFlyweight() {} - public static final int FLAGS_REQUEST_CHANNEL_C = 0b00_0100_0000; // TODO(lexs) Remove flag for initial N present public static final int FLAGS_REQUEST_CHANNEL_N = 0b00_0010_0000; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java index 07475f7f5..338738de4 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java @@ -13,10 +13,10 @@ public class KeepaliveFrameFlyweightTest { @Test public void canReadData() { ByteBuffer data = ByteBuffer.wrap(new byte[]{5, 4, 3}); - int length = KeepaliveFrameFlyweight.encode(directBuffer, 0, FrameHeaderFlyweight.FLAGS_KEEPALIVE_R, data); + int length = KeepaliveFrameFlyweight.encode(directBuffer, 0, KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R, data); data.rewind(); - assertEquals(FrameHeaderFlyweight.FLAGS_KEEPALIVE_R, FrameHeaderFlyweight.flags(directBuffer, 0) & FrameHeaderFlyweight.FLAGS_KEEPALIVE_R); + assertEquals(KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R, FrameHeaderFlyweight.flags(directBuffer, 0) & KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R); assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, length)); } } \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/FragmenterTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/FragmenterTest.java index af7a8cf22..89ac60c39 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/FragmenterTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/FragmenterTest.java @@ -21,6 +21,7 @@ import io.reactivesocket.frame.FrameHeaderFlyweight; import io.reactivesocket.frame.PayloadFragmenter; +import io.reactivesocket.frame.RequestFrameFlyweight; import org.junit.Test; import static org.junit.Assert.*; @@ -45,7 +46,7 @@ public void shouldPassThroughUnfragmentedResponse() assertEquals("response data", TestUtil.byteToString(frame1.getData())); assertEquals("response metadata", TestUtil.byteToString(frame1.getMetadata())); - assertEquals(0, (frame1.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(0, (frame1.flags() & FrameHeaderFlyweight.FLAGS_F)); assertFalse(fragmenter.hasNext()); } @@ -65,14 +66,14 @@ public void shouldHandleFragmentedResponseData() assertEquals(responseData0, TestUtil.byteToString(frame1.getData())); assertEquals("response metadata", TestUtil.byteToString(frame1.getMetadata())); - assertEquals(FrameHeaderFlyweight.FLAGS_RESPONSE_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(FrameHeaderFlyweight.FLAGS_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_F)); assertTrue(fragmenter.hasNext()); final Frame frame2 = fragmenter.next(); assertEquals(responseData1, TestUtil.byteToString(frame2.getData())); assertEquals("", TestUtil.byteToString(frame2.getMetadata())); - assertEquals(0, (frame2.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(0, (frame2.flags() & FrameHeaderFlyweight.FLAGS_F)); assertFalse(fragmenter.hasNext()); } @@ -92,14 +93,14 @@ public void shouldHandleFragmentedResponseMetadata() assertEquals("response data", TestUtil.byteToString(frame1.getData())); assertEquals(responseMetadata0, TestUtil.byteToString(frame1.getMetadata())); - assertEquals(FrameHeaderFlyweight.FLAGS_RESPONSE_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(FrameHeaderFlyweight.FLAGS_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_F)); assertTrue(fragmenter.hasNext()); final Frame frame2 = fragmenter.next(); assertEquals("", TestUtil.byteToString(frame2.getData())); assertEquals(responseMetadata1, TestUtil.byteToString(frame2.getMetadata())); - assertEquals(0, (frame2.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(0, (frame2.flags() & FrameHeaderFlyweight.FLAGS_F)); assertFalse(fragmenter.hasNext()); } @@ -122,14 +123,14 @@ public void shouldHandleFragmentedResponseMetadataAndData() assertEquals(responseData0, TestUtil.byteToString(frame1.getData())); assertEquals(responseMetadata0, TestUtil.byteToString(frame1.getMetadata())); - assertEquals(FrameHeaderFlyweight.FLAGS_RESPONSE_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(FrameHeaderFlyweight.FLAGS_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_F)); assertTrue(fragmenter.hasNext()); final Frame frame2 = fragmenter.next(); assertEquals(responseData1, TestUtil.byteToString(frame2.getData())); assertEquals(responseMetadata1, TestUtil.byteToString(frame2.getMetadata())); - assertEquals(0, (frame2.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(0, (frame2.flags() & FrameHeaderFlyweight.FLAGS_F)); assertFalse(fragmenter.hasNext()); } @@ -153,21 +154,21 @@ public void shouldHandleFragmentedResponseMetadataAndDataWithMoreThanTwoFragment assertEquals(responseData0, TestUtil.byteToString(frame1.getData())); assertEquals(responseMetadata0, TestUtil.byteToString(frame1.getMetadata())); - assertEquals(FrameHeaderFlyweight.FLAGS_RESPONSE_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(FrameHeaderFlyweight.FLAGS_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_F)); assertTrue(fragmenter.hasNext()); final Frame frame2 = fragmenter.next(); assertEquals(responseData1, TestUtil.byteToString(frame2.getData())); assertEquals(responseMetadata1, TestUtil.byteToString(frame2.getMetadata())); - assertEquals(FrameHeaderFlyweight.FLAGS_RESPONSE_F, (frame2.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(FrameHeaderFlyweight.FLAGS_F, (frame2.flags() & FrameHeaderFlyweight.FLAGS_F)); assertTrue(fragmenter.hasNext()); final Frame frame3 = fragmenter.next(); assertEquals(responseData2, TestUtil.byteToString(frame3.getData())); assertEquals("", TestUtil.byteToString(frame3.getMetadata())); - assertEquals(0, (frame3.flags() & FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + assertEquals(0, (frame3.flags() & FrameHeaderFlyweight.FLAGS_F)); assertFalse(fragmenter.hasNext()); } @@ -190,14 +191,14 @@ public void shouldHandleFragmentedRequestChannelMetadataAndData() assertEquals(requestData0, TestUtil.byteToString(frame1.getData())); assertEquals(requestMetadata0, TestUtil.byteToString(frame1.getMetadata())); - assertEquals(FrameHeaderFlyweight.FLAGS_REQUEST_CHANNEL_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_REQUEST_CHANNEL_F)); + assertEquals(FrameHeaderFlyweight.FLAGS_F, (frame1.flags() & FrameHeaderFlyweight.FLAGS_F)); assertTrue(fragmenter.hasNext()); final Frame frame2 = fragmenter.next(); assertEquals(requestData1, TestUtil.byteToString(frame2.getData())); assertEquals(requestMetadata1, TestUtil.byteToString(frame2.getMetadata())); - assertEquals(0, (frame2.flags() & FrameHeaderFlyweight.FLAGS_REQUEST_CHANNEL_F)); + assertEquals(0, (frame2.flags() & FrameHeaderFlyweight.FLAGS_F)); assertFalse(fragmenter.hasNext()); } } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java index f955d2dcb..83c8c61d8 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java @@ -57,7 +57,7 @@ public void shouldNotPassThroughFragmentedFrameIfStillMoreFollowing() final ByteBuffer metadataBuffer = TestUtil.byteBufferFromUtf8String(metadata); final ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String(data); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, dataBuffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, dataBuffer, FrameHeaderFlyweight.FLAGS_F)); //assertEquals(0, replaySubject.getValues().length); } @@ -78,7 +78,7 @@ public void shouldReassembleTwoFramesWithFragmentedDataAndMetadata() final ByteBuffer metadata1Buffer = TestUtil.byteBufferFromUtf8String(metadata1); final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_F)); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, 0)); //assertEquals(1, replaySubject.getValues().length); @@ -99,7 +99,7 @@ public void shouldReassembleTwoFramesWithFragmentedData() final ByteBuffer data0Buffer = TestUtil.byteBufferFromUtf8String(data0); final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, data0Buffer, FrameHeaderFlyweight.FLAGS_F)); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, Frame.NULL_BYTEBUFFER, data1Buffer, 0)); //assertEquals(1, replaySubject.getValues().length); @@ -120,7 +120,7 @@ public void shouldReassembleTwoFramesWithFragmentedMetadata() final ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String(data); final ByteBuffer metadata1Buffer = TestUtil.byteBufferFromUtf8String(metadata1); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, dataBuffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, dataBuffer, FrameHeaderFlyweight.FLAGS_F)); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, Frame.NULL_BYTEBUFFER, 0)); //assertEquals(1, replaySubject.getValues().length); @@ -146,8 +146,8 @@ public void shouldReassembleTwoFramesWithFragmentedDataAndMetadataWithMoreThanTw final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); final ByteBuffer data2Buffer = TestUtil.byteBufferFromUtf8String(data2); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, FrameHeaderFlyweight.FLAGS_RESPONSE_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_F)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, FrameHeaderFlyweight.FLAGS_F)); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, Frame.NULL_BYTEBUFFER, data2Buffer, 0)); //assertEquals(1, replaySubject.getValues().length); From 5d4ed2a328f174ee05638e0c44d167e659d64523 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Wed, 22 Feb 2017 18:15:34 +0000 Subject: [PATCH 008/731] Make request N non-optional for REQUEST_CHANNEL (#238) --- .../io/reactivesocket/frame/RequestFrameFlyweight.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java index 327c2dcf9..f9346c0f9 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java @@ -27,9 +27,6 @@ public class RequestFrameFlyweight { private RequestFrameFlyweight() {} - // TODO(lexs) Remove flag for initial N present - public static final int FLAGS_REQUEST_CHANNEL_N = 0b00_0010_0000; - // relative to start of passed offset private static final int INITIAL_REQUEST_N_FIELD_OFFSET = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; @@ -55,7 +52,6 @@ public static int encode( ) { final int frameLength = computeFrameLength(type, metadata.remaining(), data.remaining()); - flags |= FLAGS_REQUEST_CHANNEL_N; int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, type, streamId); mutableDirectBuffer.putInt(offset + INITIAL_REQUEST_N_FIELD_OFFSET, initialRequestN, ByteOrder.BIG_ENDIAN); @@ -76,6 +72,9 @@ public static int encode( final ByteBuffer metadata, final ByteBuffer data ) { + if (type.hasInitialRequestN()) { + throw new AssertionError(type + " must not be encoded without initial request N"); + } final int frameLength = computeFrameLength(type, metadata.remaining(), data.remaining()); int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, type, streamId); From 4d31ee1d85dd5b97448909de098d1c3261e7ede8 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Fri, 24 Feb 2017 18:04:47 +0000 Subject: [PATCH 009/731] Add test to verify wire format (#245) This test writes the expected wire format and then verifies that we encode a frame like that. I've verified by hand that the generated format is correct: 000000000000000000001001 // size (24 bits) = 9 00000000000000000000000000000101 // stream id (32 bits) = 5 001010 // frame type (6 bits) = 0x0A 0001000000 // flags (6 bits) == COMPLETE --- .../test/java/io/reactivesocket/TestUtil.java | 13 +++++++++ .../frame/FrameHeaderFlyweightTest.java | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java b/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java index 26d08c3c2..2bf81253d 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java @@ -22,6 +22,8 @@ public class TestUtil { + private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); + private TestUtil() {} public static Frame utf8EncodedRequestFrame(final int streamId, final FrameType type, final String data, final int initialRequestN) @@ -73,6 +75,17 @@ public static void copyFrame(final MutableDirectBuffer dst, final int offset, fi dst.putBytes(offset, frame.getByteBuffer(), frame.offset(), frame.length()); } + public static String bytesToHex(ByteBuffer buffer) + { + char[] hexChars = new char[buffer.limit() * 2]; + for ( int j = 0; j < buffer.limit(); j++ ) { + int v = buffer.get(j) & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + private static class PayloadImpl implements Payload // some JDK shoutout { private ByteBuffer data; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java index 1406efc6b..3f8683ff4 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java @@ -1,11 +1,16 @@ package io.reactivesocket.frame; import io.reactivesocket.FrameType; +import io.reactivesocket.TestUtil; +import org.agrona.BitUtil; import org.agrona.concurrent.UnsafeBuffer; import org.junit.Test; import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import static io.reactivesocket.TestUtil.bytesToHex; +import static io.reactivesocket.frame.FrameHeaderFlyweight.FRAME_HEADER_LENGTH; import static io.reactivesocket.frame.FrameHeaderFlyweight.NULL_BYTEBUFFER; import static org.junit.Assert.*; @@ -99,4 +104,28 @@ public void typeAndFlagTruncated() { assertEquals(flags & 0b0000_0011_1111_1111, FrameHeaderFlyweight.flags(directBuffer, 0)); assertEquals(frameType, FrameHeaderFlyweight.frameType(directBuffer, 0)); } + + @Test + public void wireFormat() { + UnsafeBuffer expectedMutable = new UnsafeBuffer(ByteBuffer.allocate(1024)); + int currentIndex = 0; + // frame length + expectedMutable.putInt(currentIndex, FrameHeaderFlyweight.FRAME_HEADER_LENGTH << 8, ByteOrder.BIG_ENDIAN); + currentIndex += 3; + // stream id + expectedMutable.putInt(currentIndex, 5, ByteOrder.BIG_ENDIAN); + currentIndex += BitUtil.SIZE_OF_INT; + // flags and frame type + expectedMutable.putShort(currentIndex, (short) 0b001010_0001000000, ByteOrder.BIG_ENDIAN); + currentIndex += BitUtil.SIZE_OF_SHORT; + + FrameType frameType = FrameType.PAYLOAD; + int flags = 0b0001000000; + FrameHeaderFlyweight.encode(directBuffer, 0, 5, flags, frameType, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + + ByteBuffer expected = ByteBufferUtil.preservingSlice(expectedMutable.byteBuffer(), 0, currentIndex); + ByteBuffer actual = ByteBufferUtil.preservingSlice(directBuffer.byteBuffer(), 0, FRAME_HEADER_LENGTH); + + assertEquals(bytesToHex(expected), bytesToHex(actual)); + } } \ No newline at end of file From a8a0b09b6ccc6ed8458e9a3a5d2ef9982b9a2ef5 Mon Sep 17 00:00:00 2001 From: somasun Date: Thu, 2 Mar 2017 03:06:52 -0800 Subject: [PATCH 010/731] fix requestSubscription override in 1.0.x (#248) --- .../src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java b/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java index 671112cc5..41e81a284 100644 --- a/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java +++ b/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java @@ -169,11 +169,6 @@ public Publisher requestStream(Payload payload) { return null; } - @Override - public Publisher requestSubscription(Payload payload) { - return null; - } - @Override public Publisher requestChannel(Publisher payloads) { return null; From 4a175592f67d0f7b9ced9cbd243b8aec55f8ccdb Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Fri, 3 Mar 2017 17:46:40 +0000 Subject: [PATCH 011/731] Set version to 1.0 (#247) --- .../frame/SetupFrameFlyweight.java | 3 +- .../frame/VersionFlyweight.java | 32 +++++++++++++++++++ .../frame/VersionFlyweightTest.java | 23 +++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/frame/VersionFlyweight.java create mode 100644 reactivesocket-core/src/test/java/io/reactivesocket/frame/VersionFlyweightTest.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java index 3d32770cd..e1435b128 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java @@ -33,8 +33,7 @@ private SetupFrameFlyweight() {} public static final int VALID_FLAGS = FLAGS_RESUME_ENABLE | FLAGS_WILL_HONOR_LEASE | FLAGS_STRICT_INTERPRETATION; - // TODO(lexs) Update this 1.0 - public static final byte CURRENT_VERSION = 0; + public static final int CURRENT_VERSION = VersionFlyweight.encode(1, 0); // relative to start of passed offset private static final int VERSION_FIELD_OFFSET = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/VersionFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/VersionFlyweight.java new file mode 100644 index 000000000..2157f8165 --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/VersionFlyweight.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivesocket.frame; + +public class VersionFlyweight { + + public static int encode(int major, int minor) { + return (major << 16) | (minor & 0xFFFF); + } + + public static int major(int version) { + return version >> 16; + } + + public static int minor(int version) { + return version & 0xFFFF; + } +} diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/VersionFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/VersionFlyweightTest.java new file mode 100644 index 000000000..60695c4a3 --- /dev/null +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/VersionFlyweightTest.java @@ -0,0 +1,23 @@ +package io.reactivesocket.frame; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class VersionFlyweightTest { + @Test + public void simple() { + int version = VersionFlyweight.encode(1, 0); + assertEquals(1, VersionFlyweight.major(version)); + assertEquals(0, VersionFlyweight.minor(version)); + assertEquals(0x00010000, version); + } + + @Test + public void complex() { + int version = VersionFlyweight.encode(0x1234, 0x5678); + assertEquals(0x1234, VersionFlyweight.major(version)); + assertEquals(0x5678, VersionFlyweight.minor(version)); + assertEquals(0x12345678, version); + } +} \ No newline at end of file From 833305b57aea356d53ab772ede060b63314b0587 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Mon, 6 Mar 2017 19:37:04 +0000 Subject: [PATCH 012/731] version pretty print (#252) --- .../src/main/java/io/reactivesocket/Frame.java | 4 +++- .../main/java/io/reactivesocket/frame/VersionFlyweight.java | 4 ++++ .../java/io/reactivesocket/frame/VersionFlyweightTest.java | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java index c4e5f2718..073330440 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java @@ -24,6 +24,7 @@ import io.reactivesocket.frame.RequestNFrameFlyweight; import io.reactivesocket.frame.SetupFrameFlyweight; import io.reactivesocket.frame.UnpooledFrame; +import io.reactivesocket.frame.VersionFlyweight; import org.agrona.DirectBuffer; import org.agrona.MutableDirectBuffer; import org.slf4j.Logger; @@ -583,7 +584,8 @@ public String toString() { additionalFlags = " Error code: " + Error.errorCode(this); break; case SETUP: - additionalFlags = " Version: " + Setup.version(this) + int version = Setup.version(this); + additionalFlags = " Version: " + VersionFlyweight.toString(version) + " keep-alive interval: " + Setup.keepaliveInterval(this) + " max lifetime: " + Setup.maxLifetime(this) + " metadata mime type: " + Setup.metadataMimeType(this) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/VersionFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/VersionFlyweight.java index 2157f8165..499e143a7 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/VersionFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/VersionFlyweight.java @@ -29,4 +29,8 @@ public static int major(int version) { public static int minor(int version) { return version & 0xFFFF; } + + public static String toString(int version) { + return major(version) + "." + minor(version); + } } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/VersionFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/VersionFlyweightTest.java index 60695c4a3..3c242ecd0 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/VersionFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/VersionFlyweightTest.java @@ -11,6 +11,7 @@ public void simple() { assertEquals(1, VersionFlyweight.major(version)); assertEquals(0, VersionFlyweight.minor(version)); assertEquals(0x00010000, version); + assertEquals("1.0", VersionFlyweight.toString(version)); } @Test @@ -19,5 +20,6 @@ public void complex() { assertEquals(0x1234, VersionFlyweight.major(version)); assertEquals(0x5678, VersionFlyweight.minor(version)); assertEquals(0x12345678, version); + assertEquals("4660.22136", VersionFlyweight.toString(version)); } } \ No newline at end of file From 29529acbce6d8943c30a661ba604609aaa531e0d Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 6 Mar 2017 11:41:45 -0800 Subject: [PATCH 013/731] 0.9-SNAPSHOT Leaving as 0.9 until we are actually ready to call it 1.0, as I don't want something like 20 1.0.0-RC#s --- gradle.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle.properties b/gradle.properties index 0a1d6bf99..c50a7a5b1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,3 +15,4 @@ # release.scope=patch +release.version=0.9-SNAPSHOT From 098332c230fb776f26ed8255abd599e5b04a9bb1 Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Tue, 7 Mar 2017 19:13:17 +0000 Subject: [PATCH 014/731] Implement Payload NEXT bit (#253) --- .../reactivesocket/ServerReactiveSocket.java | 6 +- .../frame/FrameHeaderFlyweight.java | 16 ++- .../ClientReactiveSocketTest.java | 2 +- .../java/io/reactivesocket/FrameTest.java | 8 +- .../frame/FrameHeaderFlyweightTest.java | 7 +- .../integration/IntegrationTest2.java | 105 ++++++++++++++++++ 6 files changed, 130 insertions(+), 14 deletions(-) create mode 100644 reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest2.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index fbbedc379..2b1bd8665 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -283,7 +283,7 @@ private Publisher handleRequestResponse(int streamId, Publisher r } }) .map(payload -> Frame.PayloadFrame - .from(streamId, FrameType.PAYLOAD, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C)) + .from(streamId, FrameType.NEXT_COMPLETE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C)) .doOnComplete(cleanup) .emitOnCancelOrError( // on cancel @@ -304,7 +304,7 @@ private Publisher handleRequestResponse(int streamId, Publisher r private Publisher doReceive(int streamId, Publisher response, RequestType requestType) { long now = publishSingleFrameReceiveEvents(streamId, requestType); Px resp = Px.from(response) - .map(payload -> PayloadFrame.from(streamId, FrameType.PAYLOAD, payload)); + .map(payload -> PayloadFrame.from(streamId, FrameType.NEXT, payload)); RemoteSender sender = new RemoteSender(resp, () -> subscriptions.remove(streamId), streamId, 2); subscriptions.put(streamId, sender); return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, requestType); @@ -320,7 +320,7 @@ private Publisher handleChannel(int streamId, Frame firstFrame) { Px response = Px.from(requestChannel(eventPublishingSocket.decorateReceive(streamId, receiver, RequestChannel))) - .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.PAYLOAD, payload)); + .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)); RemoteSender sender = new RemoteSender(response, () -> removeSubscriptions(streamId), streamId, initialRequestN); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java index 23fc11ad3..dc0db1ca5 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java @@ -60,6 +60,7 @@ private FrameHeaderFlyweight() {} public static final int FLAGS_F = 0b00_1000_0000; public static final int FLAGS_C = 0b00_0100_0000; + public static final int FLAGS_N = 0b00_0010_0000; static { if (INCLUDE_FRAME_LENGTH) { @@ -151,13 +152,19 @@ public static int encode( final FrameType outFrameType; switch (frameType) { + case PAYLOAD: + throw new IllegalArgumentException("Don't encode raw PAYLOAD frames, use NEXT_COMPLETE, COMPLETE or NEXT"); case NEXT_COMPLETE: + outFrameType = FrameType.PAYLOAD; + flags |= FLAGS_C | FLAGS_N; + break; case COMPLETE: outFrameType = FrameType.PAYLOAD; flags |= FLAGS_C; break; case NEXT: outFrameType = FrameType.PAYLOAD; + flags |= FLAGS_N; break; default: outFrameType = frameType; @@ -185,10 +192,15 @@ public static FrameType frameType(final DirectBuffer directBuffer, final int off final int flags = typeAndFlags & FRAME_FLAGS_MASK; boolean complete = FLAGS_C == (flags & FLAGS_C); - if (complete) { + boolean next = FLAGS_N == (flags & FLAGS_N); + if (next && complete) { result = FrameType.NEXT_COMPLETE; - } else { + } else if (complete) { + result = FrameType.COMPLETE; + } else if (next) { result = FrameType.NEXT; + } else { + throw new IllegalArgumentException("Payload must set either or both of NEXT and COMPLETE."); } } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java index 374980ed5..d54636cb8 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java @@ -122,7 +122,7 @@ public int sendRequestResponse(Publisher response) { TestSubscriber sub = TestSubscriber.create(); response.subscribe(sub); int streamId = rule.getStreamIdForRequestType(REQUEST_RESPONSE); - rule.connection.addToReceivedBuffer(Frame.PayloadFrame.from(streamId, PAYLOAD, PayloadImpl.EMPTY)); + rule.connection.addToReceivedBuffer(Frame.PayloadFrame.from(streamId, NEXT_COMPLETE, PayloadImpl.EMPTY)); sub.assertValueCount(1).assertNoErrors(); return streamId; } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java index e11c6b908..08f22cd82 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java @@ -87,7 +87,7 @@ public void testWrapMessage() { Frame f = Frame.Request.from(1, FrameType.REQUEST_RESPONSE, payload, 1); - f.wrap(2, FrameType.COMPLETE, doneBuffer); + f.wrap(2, FrameType.NEXT_COMPLETE, doneBuffer); assertEquals("done", TestUtil.byteToString(f.getData())); assertEquals(FrameType.NEXT_COMPLETE, f.getType()); assertEquals(2, f.getStreamId()); @@ -101,7 +101,7 @@ public void testWrapBytes() { final Payload anotherPayload = createPayload(Frame.NULL_BYTEBUFFER, anotherBuffer); Frame f = Frame.Request.from(1, FrameType.REQUEST_RESPONSE, payload, 1); - Frame f2 = Frame.PayloadFrame.from(20, FrameType.COMPLETE, anotherPayload); + Frame f2 = Frame.PayloadFrame.from(20, FrameType.NEXT_COMPLETE, anotherPayload); ByteBuffer b = f2.getByteBuffer(); f.wrap(b, 0); @@ -174,7 +174,7 @@ public void shouldReturnCorrectDataPlusMetadataForResponse(final int offset) final ByteBuffer requestMetadata = TestUtil.byteBufferFromUtf8String("response metadata"); final Payload payload = createPayload(requestMetadata, requestData); - Frame encodedFrame = Frame.PayloadFrame.from(1, FrameType.PAYLOAD, payload); + Frame encodedFrame = Frame.PayloadFrame.from(1, FrameType.NEXT, payload); TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); reusableFrame.wrap(reusableMutableDirectBuffer, offset); @@ -249,7 +249,7 @@ public void shouldReturnCorrectDataWithoutMetadataForResponse(final int offset) final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("response data"); final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, requestData); - Frame encodedFrame = Frame.PayloadFrame.from(1, FrameType.PAYLOAD, payload); + Frame encodedFrame = Frame.PayloadFrame.from(1, FrameType.NEXT, payload); TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); reusableFrame.wrap(reusableMutableDirectBuffer, offset); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java index 3f8683ff4..d1f8bbb81 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java @@ -116,12 +116,11 @@ public void wireFormat() { expectedMutable.putInt(currentIndex, 5, ByteOrder.BIG_ENDIAN); currentIndex += BitUtil.SIZE_OF_INT; // flags and frame type - expectedMutable.putShort(currentIndex, (short) 0b001010_0001000000, ByteOrder.BIG_ENDIAN); + expectedMutable.putShort(currentIndex, (short) 0b001010_0001100000, ByteOrder.BIG_ENDIAN); currentIndex += BitUtil.SIZE_OF_SHORT; - FrameType frameType = FrameType.PAYLOAD; - int flags = 0b0001000000; - FrameHeaderFlyweight.encode(directBuffer, 0, 5, flags, frameType, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + FrameType frameType = FrameType.NEXT_COMPLETE; + FrameHeaderFlyweight.encode(directBuffer, 0, 5, 0, frameType, NULL_BYTEBUFFER, NULL_BYTEBUFFER); ByteBuffer expected = ByteBufferUtil.preservingSlice(expectedMutable.byteBuffer(), 0, currentIndex); ByteBuffer actual = ByteBufferUtil.preservingSlice(directBuffer.byteBuffer(), 0, FRAME_HEADER_LENGTH); diff --git a/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest2.java b/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest2.java new file mode 100644 index 000000000..a52324e6d --- /dev/null +++ b/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest2.java @@ -0,0 +1,105 @@ +package io.reactivesocket.integration; + +import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.client.KeepAliveProvider; +import io.reactivesocket.client.ReactiveSocketClient; +import io.reactivesocket.client.SetupProvider; +import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; +import io.reactivesocket.server.ReactiveSocketServer; +import io.reactivesocket.transport.TransportServer; +import io.reactivesocket.transport.tcp.client.TcpTransportClient; +import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.util.PayloadImpl; +import io.reactivex.Flowable; +import io.reactivex.Single; +import org.junit.Assert; +import org.junit.Test; + +import java.util.NoSuchElementException; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; + +public class IntegrationTest2 { + // This will throw as it completes without any onNext + @Test(timeout = 2_000L, expected = NoSuchElementException.class) + public void testCompleteWithoutNext() throws InterruptedException { + ReactiveSocket requestHandler = mock(ReactiveSocket.class); + + when(requestHandler.requestStream(any())) + .thenReturn(Flowable.empty()); + + TransportServer.StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) + .start((setup, sendingSocket) -> { + Flowable.fromPublisher(sendingSocket.onClose()) + .subscribe(); + + return new DisabledLeaseAcceptingSocket(requestHandler); + }); + ReactiveSocket client = Single.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(server.getServerAddress()), + SetupProvider.keepAlive(KeepAliveProvider.never()) + .disableLease()) + .connect()) + .blockingGet(); + + Flowable.fromPublisher(client.requestStream(new PayloadImpl("REQUEST", "META"))) + .blockingFirst(); + + Thread.sleep(100); + verify(requestHandler).requestStream(new PayloadImpl("REQUEST", "META")); + } + + @Test(timeout = 2_000L) + public void testSingle() throws InterruptedException { + ReactiveSocket requestHandler = mock(ReactiveSocket.class); + + when(requestHandler.requestStream(any())) + .thenReturn(Flowable.just(new PayloadImpl("RESPONSE", "METADATA"))); + + TransportServer.StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) + .start((setup, sendingSocket) -> { + Flowable.fromPublisher(sendingSocket.onClose()) + .subscribe(); + + return new DisabledLeaseAcceptingSocket(requestHandler); + }); + ReactiveSocket client = Single.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(server.getServerAddress()), + SetupProvider.keepAlive(KeepAliveProvider.never()) + .disableLease()) + .connect()) + .blockingGet(); + + Flowable.fromPublisher(client.requestStream(new PayloadImpl("REQUEST", "META"))) + .blockingSingle(); + + verify(requestHandler).requestStream(any()); + } + + @Test() + public void testZeroPayload() throws InterruptedException { + ReactiveSocket requestHandler = mock(ReactiveSocket.class); + + when(requestHandler.requestStream(any())) + .thenReturn(Flowable.just(PayloadImpl.EMPTY)); + + TransportServer.StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) + .start((setup, sendingSocket) -> { + Flowable.fromPublisher(sendingSocket.onClose()) + .subscribe(); + + return new DisabledLeaseAcceptingSocket(requestHandler); + }); + ReactiveSocket client = Single.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(server.getServerAddress()), + SetupProvider.keepAlive(KeepAliveProvider.never()) + .disableLease()) + .connect()) + .blockingGet(); + + int dataSize = Flowable.fromPublisher(client.requestStream(new PayloadImpl("REQUEST", "META"))) + .map(p -> p.getData().remaining()) + .blockingSingle(); + + verify(requestHandler).requestStream(any()); + Assert.assertEquals(0, dataSize); + } +} From a537b08e92b5662d5a89154e1b5205626138fcbf Mon Sep 17 00:00:00 2001 From: Ryland Degnan Date: Tue, 7 Mar 2017 16:26:57 -0800 Subject: [PATCH 015/731] Reactor (#250) * Initial commit * Remove reactivesocket-publishers * Update reactivesocket-client * Build fixes * Added reactivesocket-transport-netty --- .../reactivesocket/client/LoadBalancer.java | 108 ++-- .../client/LoadBalancerInitializer.java | 57 +-- .../client/LoadBalancingClient.java | 7 +- .../client/filter/BackupRequestSocket.java | 21 +- .../client/filter/FailureAwareClient.java | 83 +-- .../client/filter/ReactiveSocketClients.java | 19 +- .../client/filter/ReactiveSockets.java | 143 ++++-- .../client/FailureReactiveSocketTest.java | 8 +- .../client/LoadBalancerInitializerTest.java | 2 +- .../client/LoadBalancerTest.java | 13 +- .../client/TestingReactiveSocket.java | 40 +- .../client/TimeoutClientTest.java | 6 +- reactivesocket-core/build.gradle | 22 +- .../io/reactivesocket/ReactiveSocketPerf.java | 46 +- .../perfutil/TestDuplexConnection.java | 28 +- .../AbstractReactiveSocket.java | 37 +- .../reactivesocket/ClientReactiveSocket.java | 113 ++--- .../io/reactivesocket/DuplexConnection.java | 15 +- .../java/io/reactivesocket/LeaseGovernor.java | 51 -- .../io/reactivesocket/ReactiveSocket.java | 16 +- .../reactivesocket/ReactiveSocketFactory.java | 3 +- .../reactivesocket/ServerReactiveSocket.java | 152 +++--- .../client/DefaultReactiveSocketClient.java | 22 +- .../client/KeepAliveProvider.java | 61 +-- .../client/ReactiveSocketClient.java | 4 +- .../reactivesocket/client/SetupProvider.java | 4 +- .../client/SetupProviderImpl.java | 87 ++-- .../events/ConnectionEventInterceptor.java | 40 +- .../events/EventPublishingSocket.java | 20 +- .../events/EventPublishingSocketImpl.java | 47 +- .../ClientServerInputMultiplexer.java | 206 +++----- .../internal/FlowControlHelper.java | 2 +- .../internal/MonoOnErrorOrCancelReturn.java | 126 +++++ .../internal/RemoteReceiver.java | 10 +- .../reactivesocket/internal/RemoteSender.java | 2 - .../internal/ValidatingSubscription.java | 2 +- .../lease/DefaultLeaseEnforcingSocket.java | 34 +- .../lease/DefaultLeaseHonoringSocket.java | 60 +-- .../lease/DisableLeaseSocket.java | 51 +- .../lease/DisabledLeaseAcceptingSocket.java | 50 +- .../lease/FairLeaseDistributor.java | 24 +- .../lease/NullLeaseGovernor.java | 34 -- .../lease/UnlimitedLeaseGovernor.java | 36 -- .../server/DefaultReactiveSocketServer.java | 70 +-- .../transport/TransportClient.java | 4 +- .../transport/TransportServer.java | 3 +- .../util/ReactiveSocketDecorator.java | 319 ------------ .../util/ReactiveSocketProxy.java | 74 +-- .../ClientReactiveSocketTest.java | 15 +- .../io/reactivesocket/ReactiveSocketTest.java | 26 +- .../ServerReactiveSocketTest.java | 11 +- .../reactivesocket/StreamIdSupplierTest.java | 16 + .../client/KeepAliveProviderTest.java | 12 +- .../client/SetupProviderImplTest.java | 15 +- .../internal/ReassemblerTest.java | 2 +- .../internal/RemoteReceiverTest.java | 2 +- .../internal/RemoteSenderTest.java | 52 +- .../DefaultLeaseEnforcingSocketTest.java | 8 +- .../lease/DefaultLeaseHonoringSocketTest.java | 7 - .../lease/DefaultLeaseTest.java | 4 +- .../lease/DisableLeaseSocketTest.java | 4 +- .../DisabledLeaseAcceptingSocketTest.java | 4 +- .../lease/FairLeaseDistributorTest.java | 20 +- .../test/util/LocalDuplexConnection.java | 37 +- .../test/util/MockReactiveSocket.java | 27 +- .../test/util/TestDuplexConnection.java | 35 +- .../discovery/eureka/Eureka.java | 2 +- .../discovery/eureka/EurekaTest.java | 4 +- reactivesocket-examples/build.gradle | 2 +- .../perf/AbstractReactiveSocketPerf.java | 12 +- .../perf/util/ClientServerHolder.java | 25 +- .../tcp/channel/ChannelEchoClient.java | 34 +- .../transport/tcp/duplex/DuplexClient.java | 31 +- .../tcp/requestresponse/HelloWorldClient.java | 27 +- .../transport/tcp/stream/StreamingClient.java | 31 +- .../transport/tcp/stress/StressTest.java | 28 +- .../tcp/stress/StressTestHandler.java | 21 +- .../transport/tcp/stress/TestConfig.java | 25 +- .../integration/IntegrationTest.java | 44 +- .../integration/IntegrationTest2.java | 105 ---- reactivesocket-publishers/build.gradle | 19 - .../extensions/DefaultSubscriber.java | 57 --- .../ExecutorServiceBasedScheduler.java | 68 --- .../reactivestreams/extensions/Px.java | 471 ------------------ .../reactivestreams/extensions/Scheduler.java | 38 -- .../extensions/TestScheduler.java | 126 ----- .../extensions/internal/Cancellable.java | 35 -- .../extensions/internal/CancellableImpl.java | 39 -- .../extensions/internal/EmptySubject.java | 104 ---- .../internal/SerializedSubscription.java | 90 ---- .../extensions/internal/package-info.java | 20 - .../ConnectableUnicastProcessor.java | 187 ------- .../internal/publishers/CachingPublisher.java | 119 ----- .../internal/publishers/ConcatPublisher.java | 83 --- .../publishers/DoOnEventPublisher.java | 153 ------ .../publishers/InstrumentingPublisher.java | 115 ----- .../publishers/SwitchToPublisher.java | 123 ----- .../internal/publishers/TimeoutPublisher.java | 76 --- .../subscribers/CancellableSubscriber.java | 24 - .../CancellableSubscriberImpl.java | 138 ----- .../internal/subscribers/Subscribers.java | 150 ------ .../extensions/ConcatTest.java | 84 ---- .../ExecutorServiceBasedSchedulerTest.java | 43 -- .../reactivestreams/extensions/PxTest.java | 74 --- .../extensions/TestSchedulerTest.java | 48 -- .../extensions/internal/EmptySubjectTest.java | 69 --- .../internal/SerializedSubscriptionTest.java | 76 --- .../publishers/ConcatPublisherTest.java | 78 --- .../publishers/DoOnEventPublisherTest.java | 149 ------ .../SingleEmissionPublishersTest.java | 104 ---- .../publishers/SwitchToPublisherTest.java | 68 --- .../publishers/TimeoutPublisherTest.java | 54 -- .../src/test/resources/log4j.properties | 33 -- .../reactivesocket/test/ClientSetupRule.java | 14 +- .../io/reactivesocket/test/PingClient.java | 20 +- .../io/reactivesocket/test/PingHandler.java | 7 +- .../test/TestReactiveSocket.java | 17 +- .../aeron/AeronDuplexConnection.java | 30 +- .../aeron/client/AeronTransportClient.java | 6 +- .../reactivestreams/AeronChannel.java | 18 +- .../AeronClientChannelConnector.java | 13 +- .../reactivestreams/AeronOutPublisher.java | 3 +- .../ReactiveStreamsRemote.java | 31 +- .../aeron/server/AeronTransportServer.java | 3 +- .../io/reactivesocket/aeron/AeronPing.java | 5 +- .../reactivestreams/AeronChannelPing.java | 16 +- .../AeronChannelPongServer.java | 7 +- .../reactivestreams/AeronChannelTest.java | 10 +- .../AeronClientServerChannelTest.java | 28 +- .../io/reactivesocket/local/LocalClient.java | 44 +- .../io/reactivesocket/local/LocalServer.java | 16 +- .../local/internal/PeerConnector.java | 65 ++- .../reactivesocket/GracefulShutdownTest.java | 2 + .../reactivesocket/test/util/LocalRSRule.java | 9 +- .../build.gradle | 3 +- .../netty/NettyDuplexConnection.java | 85 ++++ .../netty}/ReactiveSocketLengthCodec.java | 2 +- .../netty/client/TcpTransportClient.java | 53 ++ .../netty/server/TcpTransportServer.java | 85 ++++ .../transport/netty}/ClientServerTest.java | 2 +- .../transport/netty}/TcpClientSetupRule.java | 15 +- .../transport/netty}/TcpPing.java | 13 +- .../transport/netty}/TcpPongServer.java | 7 +- .../src/test/resources/log4j.properties | 0 .../transport/tcp/MutableDirectByteBuf.java | 444 ----------------- .../tcp/ReactiveSocketFrameCodec.java | 62 --- .../tcp/ReactiveSocketFrameLogger.java | 78 --- .../transport/tcp/TcpDuplexConnection.java | 65 --- .../tcp/client/TcpTransportClient.java | 87 ---- .../tcp/server/TcpTransportServer.java | 132 ----- settings.gradle | 3 +- 151 files changed, 1572 insertions(+), 6113 deletions(-) delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/LeaseGovernor.java rename {reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions => reactivesocket-core/src/main/java/io/reactivesocket}/internal/FlowControlHelper.java (97%) create mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/MonoOnErrorOrCancelReturn.java rename {reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions => reactivesocket-core/src/main/java/io/reactivesocket}/internal/ValidatingSubscription.java (98%) delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/lease/NullLeaseGovernor.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/lease/UnlimitedLeaseGovernor.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketDecorator.java delete mode 100644 reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest2.java delete mode 100644 reactivesocket-publishers/build.gradle delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/DefaultSubscriber.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/ExecutorServiceBasedScheduler.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/Px.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/Scheduler.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/TestScheduler.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/Cancellable.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/CancellableImpl.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/EmptySubject.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/SerializedSubscription.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/package-info.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/processors/ConnectableUnicastProcessor.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/CachingPublisher.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/ConcatPublisher.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/DoOnEventPublisher.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/InstrumentingPublisher.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SwitchToPublisher.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/TimeoutPublisher.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/CancellableSubscriber.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/CancellableSubscriberImpl.java delete mode 100644 reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/Subscribers.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/ConcatTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/ExecutorServiceBasedSchedulerTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/PxTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/TestSchedulerTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/EmptySubjectTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/SerializedSubscriptionTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/ConcatPublisherTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/DoOnEventPublisherTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SingleEmissionPublishersTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SwitchToPublisherTest.java delete mode 100644 reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/TimeoutPublisherTest.java delete mode 100644 reactivesocket-publishers/src/test/resources/log4j.properties rename {reactivesocket-transport-tcp => reactivesocket-transport-netty}/build.gradle (86%) create mode 100644 reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java rename {reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp => reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty}/ReactiveSocketLengthCodec.java (95%) create mode 100644 reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/client/TcpTransportClient.java create mode 100644 reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/server/TcpTransportServer.java rename {reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp => reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty}/ClientServerTest.java (97%) rename {reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp => reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty}/TcpClientSetupRule.java (72%) rename {reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp => reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty}/TcpPing.java (80%) rename {reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp => reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty}/TcpPongServer.java (79%) rename {reactivesocket-transport-tcp => reactivesocket-transport-netty}/src/test/resources/log4j.properties (100%) delete mode 100644 reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/MutableDirectByteBuf.java delete mode 100644 reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketFrameCodec.java delete mode 100644 reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketFrameLogger.java delete mode 100644 reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/TcpDuplexConnection.java delete mode 100644 reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/client/TcpTransportClient.java delete mode 100644 reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/server/TcpTransportServer.java diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java index e9d7d86dc..9211e742d 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java @@ -26,10 +26,7 @@ import io.reactivesocket.exceptions.TransportException; import io.reactivesocket.internal.DisabledEventPublisher; import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.EmptySubject; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; +import io.reactivesocket.internal.ValidatingSubscription; import io.reactivesocket.stat.Ewma; import io.reactivesocket.stat.FrugalQuantile; import io.reactivesocket.stat.Median; @@ -41,6 +38,7 @@ import org.reactivestreams.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import reactor.core.publisher.*; import java.nio.channels.ClosedChannelException; import java.util.ArrayList; @@ -93,13 +91,14 @@ public class LoadBalancer implements ReactiveSocket { private final ActiveList activeSockets; private final ActiveList activeFactories; private final FactoriesRefresher factoryRefresher; + private final Mono selectSocket; private final Ewma pendings; private volatile int targetAperture; private long lastApertureRefresh; private long refreshPeriod; private volatile long lastRefresh; - private final EmptySubject closeSubject = new EmptySubject(); + private final MonoProcessor closeSubject = MonoProcessor.create(); private final LoadBalancingClientListener eventListener; private final EventPublisher eventPublisher; @@ -152,6 +151,7 @@ public LoadBalancer( this.activeFactories = new ActiveList<>(eventListener, true); this.pendingSockets = 0; this.factoryRefresher = new FactoriesRefresher(); + this.selectSocket = Mono.fromCallable(this::select); this.minPendings = minPendings; this.maxPendings = maxPendings; @@ -193,28 +193,28 @@ public LoadBalancer(Publisher> factor } @Override - public Publisher fireAndForget(Payload payload) { - return subscriber -> select().fireAndForget(payload).subscribe(subscriber); + public Mono fireAndForget(Payload payload) { + return selectSocket.then(socket -> socket.fireAndForget(payload)); } @Override - public Publisher requestResponse(Payload payload) { - return subscriber -> select().requestResponse(payload).subscribe(subscriber); + public Mono requestResponse(Payload payload) { + return selectSocket.then(socket -> socket.requestResponse(payload)); } @Override - public Publisher requestStream(Payload payload) { - return subscriber -> select().requestStream(payload).subscribe(subscriber); + public Flux requestStream(Payload payload) { + return selectSocket.flatMap(socket -> socket.requestStream(payload)); } @Override - public Publisher metadataPush(Payload payload) { - return subscriber -> select().metadataPush(payload).subscribe(subscriber); + public Mono metadataPush(Payload payload) { + return selectSocket.then(socket -> socket.metadataPush(payload)); } @Override - public Publisher requestChannel(Publisher payloads) { - return subscriber -> select().requestChannel(payloads).subscribe(subscriber); + public Flux requestChannel(Publisher payloads) { + return selectSocket.flatMap(socket -> socket.requestChannel(payloads)); } private synchronized void addSockets(int numberOfNewSocket) { @@ -393,7 +393,7 @@ private synchronized void removeSocket(WeightedSocket socket, boolean refresh) { logger.debug("Removing socket: -> " + socket); activeSockets.remove(socket); activeFactories.add(socket.getFactory()); - socket.close().subscribe(Subscribers.empty()); + socket.close().subscribe(); if (refresh) { refreshSockets(); } @@ -492,8 +492,8 @@ public synchronized String toString() { } @Override - public Publisher close() { - return subscriber -> { + public Mono close() { + return MonoSource.wrap(subscriber -> { subscriber.onSubscribe(ValidatingSubscription.empty(subscriber)); synchronized (this) { @@ -527,11 +527,11 @@ public void onComplete() { }); }); } - }; + }); } @Override - public Publisher onClose() { + public Mono onClose() { return closeSubject; } @@ -691,31 +691,31 @@ private static class FailingReactiveSocket implements ReactiveSocket { private static final NoAvailableReactiveSocketException NO_AVAILABLE_RS_EXCEPTION = new NoAvailableReactiveSocketException(); - private static final Publisher errorVoid = Px.error(NO_AVAILABLE_RS_EXCEPTION); - private static final Publisher errorPayload = Px.error(NO_AVAILABLE_RS_EXCEPTION); + private static final Mono errorVoid = Mono.error(NO_AVAILABLE_RS_EXCEPTION); + private static final Mono errorPayload = Mono.error(NO_AVAILABLE_RS_EXCEPTION); @Override - public Publisher fireAndForget(Payload payload) { + public Mono fireAndForget(Payload payload) { return errorVoid; } @Override - public Publisher requestResponse(Payload payload) { + public Mono requestResponse(Payload payload) { return errorPayload; } @Override - public Publisher requestStream(Payload payload) { - return errorPayload; + public Flux requestStream(Payload payload) { + return errorPayload.flux(); } @Override - public Publisher requestChannel(Publisher payloads) { - return errorPayload; + public Flux requestChannel(Publisher payloads) { + return errorPayload.flux(); } @Override - public Publisher metadataPush(Payload payload) { + public Mono metadataPush(Payload payload) { return errorVoid; } @@ -725,13 +725,13 @@ public double availability() { } @Override - public Publisher close() { - return Px.empty(); + public Mono close() { + return Mono.empty(); } @Override - public Publisher onClose() { - return Px.empty(); + public Mono onClose() { + return Mono.empty(); } } @@ -743,7 +743,6 @@ private class WeightedSocket extends ReactiveSocketProxy implements LoadBalancer private static final double STARTUP_PENALTY = Long.MAX_VALUE >> 12; - private final ReactiveSocket child; private ReactiveSocketClient factory; private final Quantile lowerQuantile; private final Quantile higherQuantile; @@ -767,7 +766,6 @@ private class WeightedSocket extends ReactiveSocketProxy implements LoadBalancer int inactivityFactor ) { super(child); - this.child = child; this.factory = factory; this.lowerQuantile = lowerQuantile; this.higherQuantile = higherQuantile; @@ -780,7 +778,9 @@ private class WeightedSocket extends ReactiveSocketProxy implements LoadBalancer this.median = new Median(); this.interArrivalTime = new Ewma(1, TimeUnit.MINUTES, DEFAULT_INITIAL_INTER_ARRIVAL_TIME); this.pendingStreams = new AtomicLong(); - child.onClose().subscribe(Subscribers.doOnTerminate(() -> removeSocket(this, true))); + child.onClose() + .doFinally(signalType -> removeSocket(this, true)) + .subscribe(); } WeightedSocket( @@ -793,33 +793,33 @@ private class WeightedSocket extends ReactiveSocketProxy implements LoadBalancer } @Override - public Publisher requestResponse(Payload payload) { - return subscriber -> - child.requestResponse(payload).subscribe(new LatencySubscriber<>(subscriber, this)); + public Mono requestResponse(Payload payload) { + return MonoSource.wrap(subscriber -> + source.requestResponse(payload).subscribe(new LatencySubscriber<>(subscriber, this))); } @Override - public Publisher requestStream(Payload payload) { - return subscriber -> - child.requestStream(payload).subscribe(new CountingSubscriber<>(subscriber, this)); + public Flux requestStream(Payload payload) { + return FluxSource.wrap(subscriber -> + source.requestStream(payload).subscribe(new CountingSubscriber<>(subscriber, this))); } @Override - public Publisher fireAndForget(Payload payload) { - return subscriber -> - child.fireAndForget(payload).subscribe(new CountingSubscriber<>(subscriber, this)); + public Mono fireAndForget(Payload payload) { + return MonoSource.wrap(subscriber -> + source.fireAndForget(payload).subscribe(new CountingSubscriber<>(subscriber, this))); } @Override - public Publisher metadataPush(Payload payload) { - return subscriber -> - child.metadataPush(payload).subscribe(new CountingSubscriber<>(subscriber, this)); + public Mono metadataPush(Payload payload) { + return MonoSource.wrap(subscriber -> + source.metadataPush(payload).subscribe(new CountingSubscriber<>(subscriber, this))); } @Override - public Publisher requestChannel(Publisher payloads) { - return subscriber -> - child.requestChannel(payloads).subscribe(new CountingSubscriber<>(subscriber, this)); + public Flux requestChannel(Publisher payloads) { + return FluxSource.wrap(subscriber -> + source.requestChannel(payloads).subscribe(new CountingSubscriber<>(subscriber, this))); } ReactiveSocketClient getFactory() { @@ -893,8 +893,8 @@ private synchronized void observe(double rtt) { } @Override - public Publisher close() { - return child.close(); + public Mono close() { + return source.close(); } @Override @@ -907,7 +907,7 @@ public String toString() { + " duration/pending=" + (pending == 0 ? 0 : (double)duration / pending) + " pending=" + pending + " availability= " + availability() - + ")->" + child; + + ")->" + source; } @Override diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancerInitializer.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancerInitializer.java index 274a7add0..24b77e2b3 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancerInitializer.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancerInitializer.java @@ -19,15 +19,11 @@ import io.reactivesocket.ReactiveSocket; import io.reactivesocket.events.AbstractEventSource; import io.reactivesocket.events.ClientEventListener; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; /** * This is a temporary class to provide a {@link LoadBalancingClient#connect()} implementation when {@link LoadBalancer} @@ -35,64 +31,27 @@ */ final class LoadBalancerInitializer extends AbstractEventSource implements Runnable { - private volatile LoadBalancer loadBalancer; - private final Publisher emitSource; - private boolean ready; // Guarded by this. - private boolean created; // Guarded by this. - private final List> earlySubscribers = new CopyOnWriteArrayList<>(); + private final LoadBalancer loadBalancer; + private final MonoProcessor emitSource = MonoProcessor.create(); private LoadBalancerInitializer(Publisher> factories) { - emitSource = s -> { - final boolean _emit; - final boolean _create; - synchronized (this) { - _create = !created; - _emit = ready; - if (!_emit) { - earlySubscribers.add(s); - } - if (!created) { - created = true; - } - } - if (_create) { - loadBalancer = new LoadBalancer(factories, this, this); - } - if (_emit) { - s.onSubscribe(ValidatingSubscription.empty(s)); - s.onNext(loadBalancer); - s.onComplete(); - } - }; + loadBalancer = new LoadBalancer(factories, this, this); } static LoadBalancerInitializer create(Publisher> factories) { return new LoadBalancerInitializer(factories); } - Publisher connect() { + Mono connect() { return emitSource; } @Override public void run() { - List> earlySubs; - synchronized (this) { - if (!ready) { - earlySubs = new ArrayList<>(earlySubscribers); - earlySubscribers.clear(); - ready = true; - } else { - return; - } - } - Px source = Px.just(loadBalancer); - for (Subscriber earlySub : earlySubs) { - source.subscribe(earlySub); - } + emitSource.onNext(loadBalancer); } synchronized double availability() { - return ready? 1.0 : 0.0; + return emitSource.isTerminated() ? 1.0 : 0.0; } } diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancingClient.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancingClient.java index 823bc9818..b3acefaae 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancingClient.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancingClient.java @@ -17,8 +17,9 @@ package io.reactivesocket.client; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.Collection; @@ -41,7 +42,7 @@ public LoadBalancingClient(LoadBalancerInitializer initializer) { } @Override - public Publisher connect() { + public Mono connect() { return initializer.connect(); } @@ -65,7 +66,7 @@ public double availability() { public static LoadBalancingClient create(Publisher> servers, Function clientFactory) { SourceToClient f = new SourceToClient(clientFactory); - return new LoadBalancingClient(LoadBalancerInitializer.create(Px.from(servers).map(f))); + return new LoadBalancingClient(LoadBalancerInitializer.create(Flux.from(servers).map(f))); } /** diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/BackupRequestSocket.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/BackupRequestSocket.java index 2dde5ce50..cf8b41e6b 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/BackupRequestSocket.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/BackupRequestSocket.java @@ -23,6 +23,9 @@ import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoSource; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -51,32 +54,32 @@ public BackupRequestSocket(ReactiveSocket child) { } @Override - public Publisher fireAndForget(Payload payload) { + public Mono fireAndForget(Payload payload) { return child.fireAndForget(payload); } @Override - public Publisher requestResponse(Payload payload) { - return subscriber -> { + public Mono requestResponse(Payload payload) { + return MonoSource.wrap(subscriber -> { Subscriber oneSubscriber = new OneSubscriber<>(subscriber); Subscriber backupRequest = new FirstRequestSubscriber(oneSubscriber, () -> child.requestResponse(payload)); child.requestResponse(payload).subscribe(backupRequest); - }; + }); } @Override - public Publisher requestStream(Payload payload) { + public Flux requestStream(Payload payload) { return child.requestStream(payload); } @Override - public Publisher requestChannel(Publisher payloads) { + public Flux requestChannel(Publisher payloads) { return child.requestChannel(payloads); } @Override - public Publisher metadataPush(Payload payload) { + public Mono metadataPush(Payload payload) { return child.metadataPush(payload); } @@ -86,12 +89,12 @@ public double availability() { } @Override - public Publisher close() { + public Mono close() { return child.close(); } @Override - public Publisher onClose() { + public Mono onClose() { return child.onClose(); } diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/FailureAwareClient.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/FailureAwareClient.java index 5e0a31fee..59b60397c 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/FailureAwareClient.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/FailureAwareClient.java @@ -15,17 +15,18 @@ */ package io.reactivesocket.client.filter; +import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.AbstractReactiveSocketClient; import io.reactivesocket.client.ReactiveSocketClient; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.stat.Ewma; import io.reactivesocket.util.Clock; -import io.reactivesocket.util.ReactiveSocketDecorator; +import io.reactivesocket.util.ReactiveSocketProxy; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.concurrent.TimeUnit; -import java.util.function.Function; /** * This child compute the error rate of a particular remote location and adapt the availability @@ -57,24 +58,56 @@ public FailureAwareClient(ReactiveSocketClient delegate) { } @Override - public Publisher connect() { - return Px.from(delegate.connect()) - .doOnNext(o -> updateErrorPercentage(1.0)) - .doOnError(t -> updateErrorPercentage(0.0)) - .map(socket -> - ReactiveSocketDecorator.wrap(socket) - .availability(delegate -> { - // If the window is expired set success and failure to zero and return - // the child availability - if (Clock.now() - stamp > tau) { - updateErrorPercentage(1.0); - } - return delegate.availability() * errorPercentage.value(); - }) - .decorateAllResponses(_record()) - .decorateAllVoidResponses(_record()) - .finish() - ); + public Mono connect() { + return delegate.connect() + .doOnNext(o -> updateErrorPercentage(1.0)) + .doOnError(t -> updateErrorPercentage(0.0)) + .map(socket -> new ReactiveSocketProxy(socket) { + @Override + public Mono fireAndForget(Payload payload) { + return source.fireAndForget(payload) + .doOnError(t -> errorPercentage.insert(0.0)) + .doOnSuccess(v -> updateErrorPercentage(1.0)); + } + + @Override + public Mono requestResponse(Payload payload) { + return source.requestResponse(payload) + .doOnError(t -> errorPercentage.insert(0.0)) + .doOnSuccess(p -> updateErrorPercentage(1.0)); + } + + @Override + public Flux requestStream(Payload payload) { + return source.requestStream(payload) + .doOnError(th -> errorPercentage.insert(0.0)) + .doOnComplete(() -> updateErrorPercentage(1.0)); + } + + @Override + public Flux requestChannel(Publisher payloads) { + return source.requestChannel(payloads) + .doOnError(th -> errorPercentage.insert(0.0)) + .doOnComplete(() -> updateErrorPercentage(1.0)); + } + + @Override + public Mono metadataPush(Payload payload) { + return source.metadataPush(payload) + .doOnError(t -> errorPercentage.insert(0.0)) + .doOnSuccess(v -> updateErrorPercentage(1.0)); + } + + @Override + public double availability() { + // If the window is expired set success and failure to zero and return + // the child availability + if (Clock.now() - stamp > tau) { + updateErrorPercentage(1.0); + } + return source.availability() * errorPercentage.value(); + } + }); } @Override @@ -99,14 +132,8 @@ private synchronized void updateErrorPercentage(double value) { stamp = Clock.now(); } - private Function, Publisher> _record() { - return t -> Px.from(t) - .doOnError(th -> errorPercentage.insert(0.0)) - .doOnComplete(() -> updateErrorPercentage(1.0)); - } - @Override public String toString() { return "FailureAwareClient(" + errorPercentage.value() + ")->" + delegate; } -} +} \ No newline at end of file diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSocketClients.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSocketClients.java index d6788fc48..aba25ee38 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSocketClients.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSocketClients.java @@ -19,11 +19,9 @@ import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.AbstractReactiveSocketClient; import io.reactivesocket.client.ReactiveSocketClient; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.Scheduler; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; -import java.util.concurrent.TimeUnit; +import java.time.Duration; import java.util.function.Function; /** @@ -41,17 +39,14 @@ private ReactiveSocketClients() { * * @param orig Client to wrap. * @param timeout timeout duration. - * @param unit timeout duration unit. - * @param scheduler scheduler for timeout. * * @return New client that imposes the passed {@code timeout}. */ - public static ReactiveSocketClient connectTimeout(ReactiveSocketClient orig, long timeout, TimeUnit unit, - Scheduler scheduler) { + public static ReactiveSocketClient connectTimeout(ReactiveSocketClient orig, Duration timeout) { return new AbstractReactiveSocketClient(orig) { @Override - public Publisher connect() { - return Px.from(orig.connect()).timeout(timeout, unit, scheduler); + public Mono connect() { + return orig.connect().timeout(timeout); } @Override @@ -85,8 +80,8 @@ public static ReactiveSocketClient detectFailures(ReactiveSocketClient orig) { public static ReactiveSocketClient wrap(ReactiveSocketClient orig, Function mapper) { return new AbstractReactiveSocketClient(orig) { @Override - public Publisher connect() { - return Px.from(orig.connect()).map(mapper::apply); + public Mono connect() { + return orig.connect().map(mapper); } @Override diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSockets.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSockets.java index 56bb818a8..cf4428b16 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSockets.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSockets.java @@ -16,14 +16,14 @@ package io.reactivesocket.client.filter; +import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.Scheduler; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; -import io.reactivesocket.util.ReactiveSocketDecorator; +import io.reactivesocket.util.ReactiveSocketProxy; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; -import java.util.concurrent.TimeUnit; +import java.time.Duration; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; @@ -39,16 +39,36 @@ private ReactiveSockets() { * completed after the specified {@code timeout}. * * @param timeout timeout duration. - * @param unit timeout duration unit. - * @param scheduler scheduler for timeout. * * @return Function to transform any socket into a timeout socket. */ - public static Function timeout(long timeout, TimeUnit unit, Scheduler scheduler) { - return src -> ReactiveSocketDecorator.wrap(src) - .decorateAllResponses(_timeout(timeout, unit, scheduler)) - .decorateAllVoidResponses(_timeout(timeout, unit, scheduler)) - .finish(); + public static Function timeout(Duration timeout) { + return source -> new ReactiveSocketProxy(source) { + @Override + public Mono fireAndForget(Payload payload) { + return source.fireAndForget(payload).timeout(timeout); + } + + @Override + public Mono requestResponse(Payload payload) { + return source.requestResponse(payload).timeout(timeout); + } + + @Override + public Flux requestStream(Payload payload) { + return source.requestStream(payload).timeout(timeout); + } + + @Override + public Flux requestChannel(Publisher payloads) { + return source.requestChannel(payloads).timeout(timeout); + } + + @Override + public Mono metadataPush(Payload payload) { + return source.metadataPush(payload).timeout(timeout); + } + }; } /** @@ -59,41 +79,78 @@ public static Function timeout(long timeout, Tim * @return Function to transform any socket into a safe closing socket. */ public static Function safeClose() { - return src -> { + return source -> new ReactiveSocketProxy(source) { final AtomicInteger count = new AtomicInteger(); final AtomicBoolean closed = new AtomicBoolean(); - return ReactiveSocketDecorator.wrap(src) - .close(reactiveSocket -> - Px.defer(() -> { - if (closed.compareAndSet(false, true)) { - if (count.get() == 0) { - return src.close(); - } else { - return src.onClose(); - } - } - return src.onClose(); - }) - ) - .decorateAllResponses(_safeClose(src, closed, count)) - .decorateAllVoidResponses(_safeClose(src, closed, count)) - .finish(); - }; - } + @Override + public Mono fireAndForget(Payload payload) { + return source.fireAndForget(payload) + .doOnSubscribe(s -> count.incrementAndGet()) + .doFinally(signalType -> { + if (count.decrementAndGet() == 0 && closed.get()) { + source.close().subscribe(); + } + }); + } - private static Function, Publisher> _timeout(long timeout, TimeUnit unit, Scheduler scheduler) { - return t -> Px.from(t).timeout(timeout, unit, scheduler); - } + @Override + public Mono requestResponse(Payload payload) { + return source.requestResponse(payload) + .doOnSubscribe(s -> count.incrementAndGet()) + .doFinally(signalType -> { + if (count.decrementAndGet() == 0 && closed.get()) { + source.close().subscribe(); + } + }); + } + + @Override + public Flux requestStream(Payload payload) { + return source.requestStream(payload) + .doOnSubscribe(s -> count.incrementAndGet()) + .doFinally(signalType -> { + if (count.decrementAndGet() == 0 && closed.get()) { + source.close().subscribe(); + } + }); + } - private static Function, Publisher> _safeClose(ReactiveSocket src, AtomicBoolean closed, - AtomicInteger count) { - return t -> Px.from(t) - .doOnSubscribe(s -> count.incrementAndGet()) - .doOnTerminate(() -> { - if (count.decrementAndGet() == 0 && closed.get()) { - src.close().subscribe(Subscribers.empty()); - } - }); + @Override + public Flux requestChannel(Publisher payloads) { + return source.requestChannel(payloads) + .doOnSubscribe(s -> count.incrementAndGet()) + .doFinally(signalType -> { + if (count.decrementAndGet() == 0 && closed.get()) { + source.close().subscribe(); + } + }); + } + + @Override + public Mono metadataPush(Payload payload) { + return source.metadataPush(payload) + .doOnSubscribe(s -> count.incrementAndGet()) + .doFinally(signalType -> { + if (count.decrementAndGet() == 0 && closed.get()) { + source.close().subscribe(); + } + }); + } + + @Override + public Mono close() { + return Mono.defer(() -> { + if (closed.compareAndSet(false, true)) { + if (count.get() == 0) { + return source.close(); + } else { + return source.onClose(); + } + } + return source.onClose(); + }); + } + }; } } diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java index d2fd20297..ad1cbc595 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java @@ -23,6 +23,7 @@ import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import reactor.core.publisher.Mono; import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; @@ -116,11 +117,8 @@ private void testReactiveSocket(BiConsumer f) th }); ReactiveSocketClient factory = new AbstractReactiveSocketClient() { @Override - public Publisher connect() { - return subscriber -> { - subscriber.onNext(socket); - subscriber.onComplete(); - }; + public Mono connect() { + return Mono.just(socket); } @Override diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerInitializerTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerInitializerTest.java index aefae04bc..b0d1fef6b 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerInitializerTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerInitializerTest.java @@ -17,9 +17,9 @@ package io.reactivesocket.client; import io.reactivesocket.ReactiveSocket; -import io.reactivex.processors.UnicastProcessor; import io.reactivex.subscribers.TestSubscriber; import org.junit.Test; +import reactor.core.publisher.UnicastProcessor; import java.util.List; diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java index 7a9c6f112..e4b093432 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java @@ -18,12 +18,12 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.ReactiveSocketFactory; import org.junit.Assert; import org.junit.Test; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import reactor.core.publisher.Mono; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -68,9 +68,8 @@ public void testNeverSelectFailingSocket() throws InterruptedException { TestingReactiveSocket socket = new TestingReactiveSocket(Function.identity()); TestingReactiveSocket failingSocket = new TestingReactiveSocket(Function.identity()) { @Override - public Publisher requestResponse(Payload payload) { - return subscriber -> - subscriber.onError(new RuntimeException("You shouldn't be here")); + public Mono requestResponse(Payload payload) { + return Mono.error(new RuntimeException("You shouldn't be here")); } @Override @@ -136,8 +135,8 @@ public void onComplete() { private static ReactiveSocketClient succeedingFactory(ReactiveSocket socket) { return new AbstractReactiveSocketClient() { @Override - public Publisher connect() { - return s -> s.onNext(socket); + public Mono connect() { + return Mono.just(socket); } @Override @@ -151,7 +150,7 @@ public double availability() { private static ReactiveSocketClient failingClient(SocketAddress sa) { return new AbstractReactiveSocketClient() { @Override - public Publisher connect() { + public Mono connect() { Assert.fail(); return null; } diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/TestingReactiveSocket.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/TestingReactiveSocket.java index 5b7da1c72..03a171055 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/TestingReactiveSocket.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/TestingReactiveSocket.java @@ -18,11 +18,13 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.EmptySubject; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; +import reactor.core.publisher.MonoSource; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; @@ -31,7 +33,7 @@ public class TestingReactiveSocket implements ReactiveSocket { private final AtomicInteger count; - private final EmptySubject closeSubject = new EmptySubject(); + private final MonoProcessor closeSubject = MonoProcessor.create(); private final BiFunction, Payload, Boolean> eachPayloadHandler; public TestingReactiveSocket(Function responder) { @@ -51,13 +53,13 @@ public int countMessageReceived() { } @Override - public Publisher fireAndForget(Payload payload) { - return Px.empty(); + public Mono fireAndForget(Payload payload) { + return Mono.empty(); } @Override - public Publisher requestResponse(Payload payload) { - return subscriber -> + public Mono requestResponse(Payload payload) { + return MonoSource.wrap(subscriber -> subscriber.onSubscribe(new Subscription() { boolean cancelled; @@ -78,17 +80,17 @@ public void request(long n) { @Override public void cancel() {} - }); + })); } @Override - public Publisher requestStream(Payload payload) { - return requestResponse(payload); + public Flux requestStream(Payload payload) { + return requestResponse(payload).flux(); } @Override - public Publisher requestChannel(Publisher inputs) { - return subscriber -> + public Flux requestChannel(Publisher inputs) { + return Flux.from(subscriber -> inputs.subscribe(new Subscriber() { @Override public void onSubscribe(Subscription s) { @@ -109,11 +111,11 @@ public void onError(Throwable t) { public void onComplete() { subscriber.onComplete(); } - }); + })); } @Override - public Publisher metadataPush(Payload payload) { + public Mono metadataPush(Payload payload) { return fireAndForget(payload); } @@ -123,15 +125,15 @@ public double availability() { } @Override - public Publisher close() { - return s -> { + public Mono close() { + return Mono.defer(() -> { closeSubject.onComplete(); - closeSubject.subscribe(s); - }; + return closeSubject; + }); } @Override - public Publisher onClose() { + public Mono onClose() { return closeSubject; } } diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/TimeoutClientTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/TimeoutClientTest.java index 5859fa18b..75095a23b 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/TimeoutClientTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/TimeoutClientTest.java @@ -20,23 +20,21 @@ import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.filter.ReactiveSockets; import io.reactivesocket.exceptions.TimeoutException; -import io.reactivesocket.reactivestreams.extensions.ExecutorServiceBasedScheduler; import org.hamcrest.MatcherAssert; import org.junit.Test; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; +import java.time.Duration; import static org.hamcrest.Matchers.instanceOf; public class TimeoutClientTest { @Test public void testTimeoutSocket() { - ExecutorServiceBasedScheduler scheduler = new ExecutorServiceBasedScheduler(); TestingReactiveSocket socket = new TestingReactiveSocket((subscriber, payload) -> {return false;}); - ReactiveSocket timeout = ReactiveSockets.timeout(50, TimeUnit.MILLISECONDS, scheduler).apply(socket); + ReactiveSocket timeout = ReactiveSockets.timeout(Duration.ofMillis(50)).apply(socket); timeout.requestResponse(new Payload() { @Override diff --git a/reactivesocket-core/build.gradle b/reactivesocket-core/build.gradle index cd63edbc7..e5dbb17b6 100644 --- a/reactivesocket-core/build.gradle +++ b/reactivesocket-core/build.gradle @@ -14,30 +14,24 @@ * limitations under the License. */ -buildscript { - repositories { - maven { url "https://plugins.gradle.org/m2/" } - } - - dependencies { - classpath 'gradle.plugin.me.champeau.gradle:jmh-gradle-plugin:0.3.0' - } +plugins { + id "me.champeau.gradle.jmh" version "0.3.1" } -apply plugin: 'me.champeau.gradle.jmh' - jmh { jmhVersion = '1.15' - jvmArgs = '-XX:+UnlockCommercialFeatures -XX:+FlightRecorder' + jvmArgs = '-XX:+UseG1GC -Xmx16g -Xms16g -XX:+UnlockCommercialFeatures -XX:+FlightRecorder' profilers = ['gc'] zip64 = true warmupBatchSize = 10 iterations = 500 - + duplicateClassesStrategy = 'warn' } dependencies { - compile project(':reactivesocket-publishers') + compile 'io.projectreactor:reactor-core:3.0.5.RELEASE' + compile 'org.agrona:Agrona:0.5.4' - jmh group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.15' + jmh 'org.openjdk.jmh:jmh-core:1.15' + jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.15' } diff --git a/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java b/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java index 41e81a284..a4536bc25 100644 --- a/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java +++ b/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java @@ -22,10 +22,8 @@ import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.perfutil.TestDuplexConnection; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.transport.TransportServer; -import io.reactivex.processors.PublishProcessor; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Mode; @@ -37,6 +35,9 @@ import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -111,8 +112,8 @@ public ByteBuffer getData() { }; - static final PublishProcessor clientReceive = PublishProcessor.create(); - static final PublishProcessor serverReceive = PublishProcessor.create(); + static final DirectProcessor clientReceive = DirectProcessor.create(); + static final DirectProcessor serverReceive = DirectProcessor.create(); static final TestDuplexConnection clientConnection = new TestDuplexConnection(serverReceive, clientReceive); static final TestDuplexConnection serverConnection = new TestDuplexConnection(clientReceive, serverReceive); @@ -120,7 +121,7 @@ public ByteBuffer getData() { static final Object server = ReactiveSocketServer.create(new TransportServer() { @Override public StartedServer start(ConnectionAcceptor acceptor) { - Px.from(acceptor.apply(serverConnection)).subscribe(); + acceptor.apply(serverConnection).subscribe(); return new StartedServer() { @Override public SocketAddress getServerAddress() { @@ -155,38 +156,38 @@ public LeaseEnforcingSocket accept(ConnectionSetupPayload setup, ReactiveSocket return new DisabledLeaseAcceptingSocket(new ReactiveSocket() { @Override - public Publisher fireAndForget(Payload payload) { - return Px.empty(); + public Mono fireAndForget(Payload payload) { + return Mono.empty(); } @Override - public Publisher requestResponse(Payload payload) { - return Px.just(HELLO_PAYLOAD); + public Mono requestResponse(Payload payload) { + return Mono.just(HELLO_PAYLOAD); } @Override - public Publisher requestStream(Payload payload) { - return null; + public Flux requestStream(Payload payload) { + return Flux.empty(); } @Override - public Publisher requestChannel(Publisher payloads) { - return null; + public Flux requestChannel(Publisher payloads) { + return Flux.empty(); } @Override - public Publisher metadataPush(Payload payload) { - return null; + public Mono metadataPush(Payload payload) { + return Mono.empty(); } @Override - public Publisher close() { - return null; + public Mono close() { + return Mono.empty(); } @Override - public Publisher onClose() { - return null; + public Mono onClose() { + return Mono.empty(); } }); } @@ -221,13 +222,12 @@ public void onComplete() { }; SetupProvider setupProvider = SetupProvider.keepAlive(KeepAliveProvider.never()).disableLease(); - ReactiveSocketClient reactiveSocketClient = ReactiveSocketClient.create(() -> Px.just(clientConnection), setupProvider); + ReactiveSocketClient reactiveSocketClient = ReactiveSocketClient.create(() -> Mono.just(clientConnection), setupProvider); CountDownLatch latch = new CountDownLatch(1); - Px - .from(reactiveSocketClient.connect()) + reactiveSocketClient.connect() .doOnNext(r -> this.client = r) - .doOnComplete(latch::countDown) + .doFinally(signalType -> latch.countDown()) .subscribe(); try { diff --git a/reactivesocket-core/src/jmh/java/io/reactivesocket/perfutil/TestDuplexConnection.java b/reactivesocket-core/src/jmh/java/io/reactivesocket/perfutil/TestDuplexConnection.java index 083e7e171..5af745aac 100644 --- a/reactivesocket-core/src/jmh/java/io/reactivesocket/perfutil/TestDuplexConnection.java +++ b/reactivesocket-core/src/jmh/java/io/reactivesocket/perfutil/TestDuplexConnection.java @@ -18,37 +18,37 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivex.processors.PublishProcessor; -import io.reactivex.subjects.PublishSubject; import org.reactivestreams.Publisher; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; /** * An implementation of {@link DuplexConnection} that provides functionality to modify the behavior dynamically. */ public class TestDuplexConnection implements DuplexConnection { - private final PublishProcessor send; - private final PublishProcessor receive; + private final DirectProcessor send; + private final DirectProcessor receive; - public TestDuplexConnection(PublishProcessor send, PublishProcessor receive) { + public TestDuplexConnection(DirectProcessor send, DirectProcessor receive) { this.send = send; this.receive = receive; } @Override - public Publisher send(Publisher frame) { - Px + public Mono send(Publisher frame) { + Flux .from(frame) .doOnNext(f -> send.onNext(f)) .doOnError(t -> {throw new RuntimeException(t); }) .subscribe(); - return Px.empty(); + return Mono.empty(); } @Override - public Publisher receive() { + public Flux receive() { return receive; } @@ -58,12 +58,12 @@ public double availability() { } @Override - public Publisher close() { - return Px.empty(); + public Mono close() { + return Mono.empty(); } @Override - public Publisher onClose() { - return Px.empty(); + public Mono onClose() { + return Mono.empty(); } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/AbstractReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/AbstractReactiveSocket.java index 86fd4ec88..69ad73e49 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/AbstractReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/AbstractReactiveSocket.java @@ -16,9 +16,10 @@ package io.reactivesocket; -import io.reactivesocket.reactivestreams.extensions.internal.EmptySubject; import org.reactivestreams.Publisher; -import io.reactivesocket.reactivestreams.extensions.Px; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; /** * An abstract implementation of {@link ReactiveSocket}. All request handling methods emit @@ -28,43 +29,43 @@ */ public abstract class AbstractReactiveSocket implements ReactiveSocket { - private final EmptySubject onClose = new EmptySubject(); + private final MonoProcessor onClose = MonoProcessor.create(); @Override - public Publisher fireAndForget(Payload payload) { - return Px.error(new UnsupportedOperationException("Fire and forget not implemented.")); + public Mono fireAndForget(Payload payload) { + return Mono.error(new UnsupportedOperationException("Fire and forget not implemented.")); } @Override - public Publisher requestResponse(Payload payload) { - return Px.error(new UnsupportedOperationException("Request-Response not implemented.")); + public Mono requestResponse(Payload payload) { + return Mono.error(new UnsupportedOperationException("Request-Response not implemented.")); } @Override - public Publisher requestStream(Payload payload) { - return Px.error(new UnsupportedOperationException("Request-Stream not implemented.")); + public Flux requestStream(Payload payload) { + return Flux.error(new UnsupportedOperationException("Request-Stream not implemented.")); } @Override - public Publisher requestChannel(Publisher payloads) { - return Px.error(new UnsupportedOperationException("Request-Channel not implemented.")); + public Flux requestChannel(Publisher payloads) { + return Flux.error(new UnsupportedOperationException("Request-Channel not implemented.")); } @Override - public Publisher metadataPush(Payload payload) { - return Px.error(new UnsupportedOperationException("Metadata-Push not implemented.")); + public Mono metadataPush(Payload payload) { + return Mono.error(new UnsupportedOperationException("Metadata-Push not implemented.")); } @Override - public Publisher close() { - return s -> { + public Mono close() { + return Mono.defer(() -> { onClose.onComplete(); - onClose.subscribe(s); - }; + return onClose; + }); } @Override - public Publisher onClose() { + public Mono onClose() { return onClose; } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java index 69561f5d6..09c669ff0 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java @@ -18,35 +18,27 @@ import io.reactivesocket.client.KeepAliveProvider; import io.reactivesocket.events.EventListener; -import io.reactivesocket.events.EventListener.RequestType; import io.reactivesocket.events.EventPublishingSocket; import io.reactivesocket.events.EventPublishingSocketImpl; import io.reactivesocket.exceptions.CancelException; import io.reactivesocket.exceptions.Exceptions; -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.internal.KnownErrorFilter; -import io.reactivesocket.internal.RemoteReceiver; -import io.reactivesocket.internal.RemoteSender; +import io.reactivesocket.internal.*; import io.reactivesocket.lease.Lease; import io.reactivesocket.lease.LeaseImpl; -import io.reactivesocket.reactivestreams.extensions.DefaultSubscriber; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.FlowControlHelper; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.CancellableSubscriber; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; import org.agrona.collections.Int2ObjectHashMap; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoSource; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import static io.reactivesocket.events.EventListener.RequestType.*; -import static io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers.*; /** * Client Side of a ReactiveSocket socket. Sends {@link Frame}s @@ -61,10 +53,10 @@ public class ClientReactiveSocket implements ReactiveSocket { private final EventPublishingSocket eventPublishingSocket; private final Int2ObjectHashMap senders; - private final Int2ObjectHashMap> receivers; + private final Int2ObjectHashMap> receivers; private final BufferingSubscription transportReceiveSubscription = new BufferingSubscription(); - private CancellableSubscriber keepAliveSendSub; + private Disposable keepAliveSendSub; private volatile Consumer leaseConsumer; // Provided on start() public ClientReactiveSocket(DuplexConnection connection, Consumer errorConsumer, @@ -78,9 +70,9 @@ public ClientReactiveSocket(DuplexConnection connection, Consumer err : EventPublishingSocket.DISABLED; senders = new Int2ObjectHashMap<>(256, 0.9f); receivers = new Int2ObjectHashMap<>(256, 0.9f); - connection.onClose().subscribe(Subscribers.cleanup(() -> { - cleanup(); - })); + connection.onClose() + .doFinally(signalType -> cleanup()) + .subscribe(); } public ClientReactiveSocket(DuplexConnection connection, Consumer errorConsumer, @@ -89,8 +81,8 @@ public ClientReactiveSocket(DuplexConnection connection, Consumer err } @Override - public Publisher fireAndForget(Payload payload) { - return Px.defer(() -> { + public Mono fireAndForget(Payload payload) { + return Mono.defer(() -> { final int streamId = nextStreamId(); final Frame requestFrame = Frame.Request.from(streamId, FrameType.FIRE_AND_FORGET, payload, 0); return connection.sendOne(requestFrame); @@ -98,22 +90,22 @@ public Publisher fireAndForget(Payload payload) { } @Override - public Publisher requestResponse(Payload payload) { + public Mono requestResponse(Payload payload) { return handleRequestResponse(payload); } @Override - public Publisher requestStream(Payload payload) { - return handleStreamResponse(Px.just(payload), FrameType.REQUEST_STREAM); + public Flux requestStream(Payload payload) { + return handleStreamResponse(Flux.just(payload), FrameType.REQUEST_STREAM); } @Override - public Publisher requestChannel(Publisher payloads) { - return handleStreamResponse(Px.from(payloads), FrameType.REQUEST_CHANNEL); + public Flux requestChannel(Publisher payloads) { + return handleStreamResponse(Flux.from(payloads), FrameType.REQUEST_CHANNEL); } @Override - public Publisher metadataPush(Payload payload) { + public Mono metadataPush(Payload payload) { final Frame requestFrame = Frame.Request.from(0, FrameType.METADATA_PUSH, payload, 0); return connection.sendOne(requestFrame); } @@ -124,15 +116,12 @@ public double availability() { } @Override - public Publisher close() { - return Px.concatEmpty(Px.defer(() -> { - cleanup(); - return Px.empty(); - }), connection.close()); + public Mono close() { + return connection.close(); } @Override - public Publisher onClose() { + public Mono onClose() { return connection.onClose(); } @@ -143,48 +132,35 @@ public ClientReactiveSocket start(Consumer leaseConsumer) { return this; } - private Publisher handleRequestResponse(final Payload payload) { - return Px.create(subscriber -> { + private Mono handleRequestResponse(final Payload payload) { + return MonoSource.wrap(subscriber -> { int streamId = nextStreamId(); final Frame requestFrame = Frame.Request.from(streamId, FrameType.REQUEST_RESPONSE, payload, 1); synchronized (this) { - @SuppressWarnings("rawtypes") - Subscriber raw = subscriber; - @SuppressWarnings("unchecked") - Subscriber fs = raw; - receivers.put(streamId, fs); + receivers.put(streamId, subscriber); } - Publisher send = eventPublishingSocket.decorateSend(streamId, connection.sendOne(requestFrame), 0, + Mono send = eventPublishingSocket.decorateSend(streamId, connection.sendOne(requestFrame), 0, RequestResponse); - eventPublishingSocket.decorateReceive(streamId, Px.concatEmpty(send, Px.never()) - .cast(Payload.class) - .doOnCancel(() -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.Cancel.from(streamId)) - .subscribe(DefaultSubscriber.defaultInstance()); - } - removeReceiver(streamId); - }), RequestResponse).subscribe(subscriber); + eventPublishingSocket.decorateReceive(streamId, send.then(Mono.never()) + .doOnCancel(() -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(); + } + removeReceiver(streamId); + }), RequestResponse).subscribe(subscriber); }); } - private Publisher handleStreamResponse(Px request, FrameType requestType) { - return Px.defer(() -> { + private Flux handleStreamResponse(Flux request, FrameType requestType) { + return Flux.defer(() -> { int streamId = nextStreamId(); RemoteSender sender = new RemoteSender(request.map(payload -> Frame.Request.from(streamId, requestType, payload, 1)), removeSenderLambda(streamId), streamId, 1); Publisher src = s -> { - CancellableSubscriber sendSub = doOnError(throwable -> { - s.onError(throwable); - }); - ValidatingSubscription sub = ValidatingSubscription.create(s, () -> { - sendSub.cancel(); - }, requestN -> { - transportReceiveSubscription.request(requestN); - }); - eventPublishingSocket.decorateSend(streamId, connection.send(sender), 0, - fromFrameType(requestType)).subscribe(sendSub); + Disposable disposable = eventPublishingSocket.decorateSend(streamId, connection.send(sender), 0, fromFrameType(requestType)) + .subscribe(null, s::onError); + ValidatingSubscription sub = ValidatingSubscription.create(s, disposable::dispose, transportReceiveSubscription::request); s.onSubscribe(sub); }; @@ -196,24 +172,23 @@ private Publisher handleStreamResponse(Px request, FrameType r } private void startKeepAlive() { - keepAliveSendSub = doOnError(errorConsumer); - connection.send(Px.from(keepAliveProvider.ticks()) + keepAliveSendSub = connection.send(keepAliveProvider.ticks() .map(i -> Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, true))) - .subscribe(keepAliveSendSub); + .subscribe(null, errorConsumer); } private void startReceivingRequests() { - Px - .from(connection.receive()) - .doOnSubscribe(subscription -> transportReceiveSubscription.switchTo(subscription)) + connection.receive() + .doOnSubscribe(transportReceiveSubscription::switchTo) .doOnNext(this::handleIncomingFrames) + .doOnError(errorConsumer) .subscribe(); } protected void cleanup() { // TODO: Stop sending requests first if (null != keepAliveSendSub) { - keepAliveSendSub.cancel(); + keepAliveSendSub.dispose(); } transportReceiveSubscription.cancel(); } @@ -253,7 +228,7 @@ private void handleStreamZero(FrameType type, Frame frame) { @SuppressWarnings("unchecked") private void handleFrame(int streamId, FrameType type, Frame frame) { - Subscriber receiver; + Subscriber receiver; synchronized (this) { receiver = receivers.get(streamId); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/DuplexConnection.java b/reactivesocket-core/src/main/java/io/reactivesocket/DuplexConnection.java index aa452fc60..8d5a6a8c8 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/DuplexConnection.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/DuplexConnection.java @@ -17,7 +17,8 @@ import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; -import io.reactivesocket.reactivestreams.extensions.Px; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.nio.channels.ClosedChannelException; @@ -39,7 +40,7 @@ public interface DuplexConnection extends Availability { * @return {@code Publisher} that completes when all the frames are written on the connection successfully and * errors when it fails. */ - Publisher send(Publisher frame); + Mono send(Publisher frame); /** * Sends a single {@code Frame} on this connection and returns the {@code Publisher} representing the result @@ -50,8 +51,8 @@ public interface DuplexConnection extends Availability { * @return {@code Publisher} that completes when the frame is written on the connection successfully and errors * when it fails. */ - default Publisher sendOne(Frame frame) { - return send(Px.just(frame)); + default Mono sendOne(Frame frame) { + return send(Mono.just(frame)); } /** @@ -75,7 +76,7 @@ default Publisher sendOne(Frame frame) { * * @return Stream of all {@code Frame}s received. */ - Publisher receive(); + Flux receive(); /** * Close this {@code DuplexConnection} upon subscribing to the returned {@code Publisher} @@ -84,12 +85,12 @@ default Publisher sendOne(Frame frame) { * * @return A {@code Publisher} that completes when this {@code DuplexConnection} close is complete. */ - Publisher close(); + Mono close(); /** * Returns a {@code Publisher} that completes when this {@code DuplexConnection} is closed. * * @return A {@code Publisher} that completes when this {@code DuplexConnection} close is complete. */ - Publisher onClose(); + Mono onClose(); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/LeaseGovernor.java b/reactivesocket-core/src/main/java/io/reactivesocket/LeaseGovernor.java deleted file mode 100644 index e56b6898b..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/LeaseGovernor.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket; - -import io.reactivesocket.lease.NullLeaseGovernor; -import io.reactivesocket.lease.UnlimitedLeaseGovernor; - -public interface LeaseGovernor { - LeaseGovernor NULL_LEASE_GOVERNOR = new NullLeaseGovernor(); - LeaseGovernor UNLIMITED_LEASE_GOVERNOR = new UnlimitedLeaseGovernor(); - - /** - * Register a responder into the LeaseGovernor. - * This give the responsibility to the leaseGovernor to send lease to the responder. - * - * @param responder the responder that will receive lease - */ - // void register(Responder responder); - - /** - * Unregister a responder from the LeaseGovernor. - * Depending on the implementation, this action may trigger a rebalancing of - * the tickets/window to the remaining responders. - * @param responder the responder to be removed - */ - //void unregister(Responder responder); - - /** - * Check if the message received by the responder is valid (i.e. received during a - * valid lease window) - * This action my have side effect in the LeaseGovernor. - * - * @param responder receiving the message - * @param frame the received frame - * @return - */ - // boolean accept(Responder responder, Frame frame); -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocket.java index b7198a70a..80015a91c 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocket.java @@ -17,6 +17,8 @@ package io.reactivesocket; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; /** * A contract providing different interaction models for ReactiveSocket protocol. @@ -30,7 +32,7 @@ public interface ReactiveSocket extends Availability { * * @return {@code Publisher} that completes when the passed {@code payload} is successfully handled, otherwise errors. */ - Publisher fireAndForget(Payload payload); + Mono fireAndForget(Payload payload); /** * Request-Response interaction model of {@code ReactiveSocket}. @@ -39,7 +41,7 @@ public interface ReactiveSocket extends Availability { * * @return {@code Publisher} containing at most a single {@code Payload} representing the response. */ - Publisher requestResponse(Payload payload); + Mono requestResponse(Payload payload); /** * Request-Stream interaction model of {@code ReactiveSocket}. @@ -48,7 +50,7 @@ public interface ReactiveSocket extends Availability { * * @return {@code Publisher} containing the stream of {@code Payload}s representing the response. */ - Publisher requestStream(Payload payload); + Flux requestStream(Payload payload); /** * Request-Channel interaction model of {@code ReactiveSocket}. @@ -57,7 +59,7 @@ public interface ReactiveSocket extends Availability { * * @return Stream of response payloads. */ - Publisher requestChannel(Publisher payloads); + Flux requestChannel(Publisher payloads); /** * Metadata-Push interaction model of {@code ReactiveSocket}. @@ -66,7 +68,7 @@ public interface ReactiveSocket extends Availability { * * @return {@code Publisher} that completes when the passed {@code payload} is successfully handled, otherwise errors. */ - Publisher metadataPush(Payload payload); + Mono metadataPush(Payload payload); @Override default double availability() { @@ -80,7 +82,7 @@ default double availability() { * * @return A {@code Publisher} that completes when this {@code ReactiveSocket} close is complete. */ - Publisher close(); + Mono close(); /** * Returns a {@code Publisher} that completes when this {@code ReactiveSocket} is closed. A {@code ReactiveSocket} @@ -88,5 +90,5 @@ default double availability() { * * @return A {@code Publisher} that completes when this {@code ReactiveSocket} close is complete. */ - Publisher onClose(); + Mono onClose(); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocketFactory.java b/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocketFactory.java index 4f47af1df..679943275 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocketFactory.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocketFactory.java @@ -16,6 +16,7 @@ package io.reactivesocket; import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; /** * Factory of ReactiveSocket interface @@ -29,7 +30,7 @@ public interface ReactiveSocketFactory { * * @return A source that emits a single {@code ReactiveSocket}. */ - Publisher apply(); + Mono apply(); /** * @return a positive numbers representing the availability of the factory. diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index 2b1bd8665..f60f5a153 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -26,18 +26,14 @@ import io.reactivesocket.exceptions.ApplicationException; import io.reactivesocket.exceptions.SetupException; import io.reactivesocket.frame.FrameHeaderFlyweight; -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.internal.KnownErrorFilter; -import io.reactivesocket.internal.RemoteReceiver; -import io.reactivesocket.internal.RemoteSender; +import io.reactivesocket.internal.*; import io.reactivesocket.lease.LeaseEnforcingSocket; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; import io.reactivesocket.util.Clock; import org.agrona.collections.Int2ObjectHashMap; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.Collection; import java.util.function.Consumer; @@ -51,7 +47,7 @@ public class ServerReactiveSocket implements ReactiveSocket { private final DuplexConnection connection; - private final Publisher serverInput; + private final Flux serverInput; private final Consumer errorConsumer; private final EventPublisher eventPublisher; @@ -75,9 +71,9 @@ public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestH eventPublishingSocket = eventPublisher.isEventPublishingEnabled()? new EventPublishingSocketImpl(eventPublisher, false) : EventPublishingSocket.DISABLED; - Px.from(connection.onClose()).subscribe(Subscribers.cleanup(() -> { - cleanup(); - })); + connection.onClose() + .doFinally(signalType -> cleanup()) + .subscribe(); if (requestHandler instanceof LeaseEnforcingSocket) { LeaseEnforcingSocket enforcer = (LeaseEnforcingSocket) requestHandler; enforcer.acceptLeaseSender(lease -> { @@ -85,7 +81,7 @@ public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestH return; } Frame leaseFrame = Lease.from(lease.getTtl(), lease.getAllowedRequests(), lease.metadata()); - Px.from(connection.sendOne(leaseFrame)) + connection.sendOne(leaseFrame) .doOnError(errorConsumer) .subscribe(); }); @@ -102,47 +98,44 @@ public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestH } @Override - public Publisher fireAndForget(Payload payload) { + public Mono fireAndForget(Payload payload) { return requestHandler.fireAndForget(payload); } @Override - public Publisher requestResponse(Payload payload) { + public Mono requestResponse(Payload payload) { return requestHandler.requestResponse(payload); } @Override - public Publisher requestStream(Payload payload) { + public Flux requestStream(Payload payload) { return requestHandler.requestStream(payload); } @Override - public Publisher requestChannel(Publisher payloads) { + public Flux requestChannel(Publisher payloads) { return requestHandler.requestChannel(payloads); } @Override - public Publisher metadataPush(Payload payload) { + public Mono metadataPush(Payload payload) { return requestHandler.metadataPush(payload); } @Override - public Publisher close() { - return Px.concatEmpty(Px.defer(() -> { - cleanup(); - return Px.empty(); - }), connection.close()); + public Mono close() { + return connection.close(); } @Override - public Publisher onClose() { + public Mono onClose() { return connection.onClose(); } public ServerReactiveSocket start() { - Px.from(serverInput) + serverInput .doOnNext(frame -> { - handleFrame(frame).subscribe(Subscribers.doOnError(errorConsumer)); + handleFrame(frame).doOnError(errorConsumer).subscribe(); }) .doOnError(t -> { errorConsumer.accept(t); @@ -157,29 +150,19 @@ public ServerReactiveSocket start() { .forEach(Subscription::cancel); }) .doOnSubscribe(subscription -> { - receiversSubscription = new Subscription() { - @Override - public void request(long n) { - subscription.request(n); - } - - @Override - public void cancel() { - subscription.cancel(); - } - }; + receiversSubscription = subscription; }) .subscribe(); return this; } - private Publisher handleFrame(Frame frame) { + private Mono handleFrame(Frame frame) { final int streamId = frame.getStreamId(); try { RemoteReceiver receiver; switch (frame.getType()) { case SETUP: - return Px.error(new IllegalStateException("Setup frame received post setup.")); + return Mono.error(new IllegalStateException("Setup frame received post setup.")); case REQUEST_RESPONSE: return handleRequestResponse(streamId, requestResponse(frame)); case CANCEL: @@ -196,12 +179,12 @@ private Publisher handleFrame(Frame frame) { return handleChannel(streamId, frame); case PAYLOAD: // TODO: Hook in receiving socket. - return Px.empty(); + return Mono.empty(); case METADATA_PUSH: return metadataPush(frame); case LEASE: // Lease must not be received here as this is the server end of the socket which sends leases. - return Px.empty(); + return Mono.empty(); case NEXT: synchronized (channelProcessors) { receiver = channelProcessors.get(streamId); @@ -209,7 +192,7 @@ private Publisher handleFrame(Frame frame) { if (receiver != null) { receiver.onNext(frame); } - return Px.empty(); + return Mono.empty(); case COMPLETE: synchronized (channelProcessors) { receiver = channelProcessors.get(streamId); @@ -217,7 +200,7 @@ private Publisher handleFrame(Frame frame) { if (receiver != null) { receiver.onComplete(); } - return Px.empty(); + return Mono.empty(); case ERROR: synchronized (channelProcessors) { receiver = channelProcessors.get(streamId); @@ -225,7 +208,7 @@ private Publisher handleFrame(Frame frame) { if (receiver != null) { receiver.onError(new ApplicationException(frame)); } - return Px.empty(); + return Mono.empty(); case NEXT_COMPLETE: synchronized (channelProcessors) { receiver = channelProcessors.get(streamId); @@ -234,16 +217,16 @@ private Publisher handleFrame(Frame frame) { receiver.onNext(frame); receiver.onComplete(); } - return Px.empty(); + return Mono.empty(); default: return handleError(streamId, new IllegalStateException("ServerReactiveSocket: Unexpected frame type: " + frame.getType())); } } catch (Throwable t) { - Publisher toReturn = handleError(streamId, t); + Mono toReturn = handleError(streamId, t); // If it's a setup exception re-throw the exception to tear everything down if (t instanceof SetupException) { - toReturn = Px.concatEmpty(toReturn, Px.error(t)); + toReturn = toReturn.thenEmpty(Mono.error(t)); } return toReturn; } @@ -262,55 +245,41 @@ private synchronized void cleanup() { subscriptions.clear(); channelProcessors.values().forEach(RemoteReceiver::cancel); subscriptions.clear(); - requestHandler.close().subscribe(Subscribers.empty()); + requestHandler.close().subscribe(); } - private Publisher handleRequestResponse(int streamId, Publisher response) { - final Runnable cleanup = () -> { - synchronized (this) { - subscriptions.remove(streamId); - } - - }; + private Mono handleRequestResponse(int streamId, Mono response) { long now = publishSingleFrameReceiveEvents(streamId, RequestResponse); - Px frames = - Px - .from(response) + Mono frames = new MonoOnErrorOrCancelReturn<>( + response .doOnSubscribe(subscription -> { synchronized (this) { subscriptions.put(streamId, subscription); } - }) - .map(payload -> Frame.PayloadFrame - .from(streamId, FrameType.NEXT_COMPLETE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C)) - .doOnComplete(cleanup) - .emitOnCancelOrError( - // on cancel - () -> { - cleanup.run(); - return Frame.Cancel.from(streamId); - }, - // on error - throwable -> { - cleanup.run(); - return Frame.Error.from(streamId, throwable); - }); - - return Px.from(eventPublishingSocket.decorateSend(streamId, connection.send(frames), now, RequestResponse)); + }).map(payload -> + Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C) + ).doFinally(signalType -> { + synchronized (this) { + subscriptions.remove(streamId); + } + }), + throwable -> Frame.Error.from(streamId, throwable), + () -> Frame.Cancel.from(streamId) + ); + return eventPublishingSocket.decorateSend(streamId, connection.send(frames), now, RequestResponse); } - private Publisher doReceive(int streamId, Publisher response, RequestType requestType) { + private Mono doReceive(int streamId, Flux response, RequestType requestType) { long now = publishSingleFrameReceiveEvents(streamId, requestType); - Px resp = Px.from(response) - .map(payload -> PayloadFrame.from(streamId, FrameType.NEXT, payload)); + Flux resp = response.map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)); RemoteSender sender = new RemoteSender(resp, () -> subscriptions.remove(streamId), streamId, 2); subscriptions.put(streamId, sender); return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, requestType); } - private Publisher handleChannel(int streamId, Frame firstFrame) { + private Mono handleChannel(int streamId, Frame firstFrame) { long now = publishSingleFrameReceiveEvents(streamId, RequestChannel); int initialRequestN = Request.initialRequestN(firstFrame); Frame firstAsNext = Request.from(streamId, FrameType.NEXT, firstFrame, initialRequestN); @@ -318,8 +287,7 @@ private Publisher handleChannel(int streamId, Frame firstFrame) { firstAsNext, receiversSubscription, true); channelProcessors.put(streamId, receiver); - Px response = Px.from(requestChannel(eventPublishingSocket.decorateReceive(streamId, receiver, - RequestChannel))) + Flux response = requestChannel(eventPublishingSocket.decorateReceive(streamId, receiver, RequestChannel)) .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)); RemoteSender sender = new RemoteSender(response, () -> removeSubscriptions(streamId), streamId, @@ -331,25 +299,25 @@ private Publisher handleChannel(int streamId, Frame firstFrame) { return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, RequestChannel); } - private Publisher handleFireAndForget(int streamId, Publisher result) { - return Px.from(result) + private Mono handleFireAndForget(int streamId, Mono result) { + return result .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) .doOnError(t -> { removeSubscription(streamId); errorConsumer.accept(t); }) - .doOnComplete(() -> removeSubscription(streamId)); + .doFinally(signalType -> removeSubscription(streamId)); } - private Publisher handleKeepAliveFrame(Frame frame) { + private Mono handleKeepAliveFrame(Frame frame) { if (Frame.Keepalive.hasRespondFlag(frame)) { - return Px.from(connection.sendOne(Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, false))) + return connection.sendOne(Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, false)) .doOnError(errorConsumer); } - return Px.empty(); + return Mono.empty(); } - private Publisher handleCancelFrame(int streamId) { + private Mono handleCancelFrame(int streamId) { Subscription subscription; synchronized (this) { subscription = subscriptions.remove(streamId); @@ -359,15 +327,15 @@ private Publisher handleCancelFrame(int streamId) { subscription.cancel(); } - return Px.empty(); + return Mono.empty(); } - private Publisher handleError(int streamId, Throwable t) { - return Px.from(connection.sendOne(Frame.Error.from(streamId, t))) + private Mono handleError(int streamId, Throwable t) { + return connection.sendOne(Frame.Error.from(streamId, t)) .doOnError(errorConsumer); } - private Px handleRequestN(int streamId, Frame frame) { + private Mono handleRequestN(int streamId, Frame frame) { Subscription subscription; synchronized (this) { subscription = subscriptions.get(streamId); @@ -376,7 +344,7 @@ private Px handleRequestN(int streamId, Frame frame) { int n = Frame.RequestN.requestN(frame); subscription.request(n >= Integer.MAX_VALUE ? Long.MAX_VALUE : n); } - return Px.empty(); + return Mono.empty(); } private synchronized void addSubscription(int streamId, Subscription subscription) { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java index 906fd338c..e6e53aa75 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java @@ -17,18 +17,8 @@ package io.reactivesocket.client; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.events.ClientEventListener; -import io.reactivesocket.events.ConnectionEventInterceptor; -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.publishers.InstrumentingPublisher; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; import io.reactivesocket.transport.TransportClient; -import io.reactivesocket.util.Clock; -import org.reactivestreams.Publisher; - -import static java.util.concurrent.TimeUnit.*; +import reactor.core.publisher.Mono; /** * Default implementation of {@link ReactiveSocketClient} providing the functionality to create a {@link ReactiveSocket} @@ -36,19 +26,17 @@ */ public final class DefaultReactiveSocketClient extends AbstractReactiveSocketClient { - private final Px connectSource; + private final Mono connectSource; public DefaultReactiveSocketClient(TransportClient transportClient, SetupProvider setupProvider, SocketAcceptor acceptor) { super(setupProvider); - connectSource = Px.from(transportClient.connect()) - .switchTo(connection -> { - return setupProvider.accept(connection, acceptor); - }); + connectSource = transportClient.connect() + .then(connection -> setupProvider.accept(connection, acceptor)); } @Override - public Publisher connect() { + public Mono connect() { return connectSource; } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/KeepAliveProvider.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/KeepAliveProvider.java index 607f0affb..9e7c9768c 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/KeepAliveProvider.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/KeepAliveProvider.java @@ -17,10 +17,7 @@ package io.reactivesocket.client; import io.reactivesocket.exceptions.ConnectionException; -import io.reactivesocket.reactivestreams.extensions.Px; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; +import reactor.core.publisher.Flux; import java.util.function.LongSupplier; @@ -35,46 +32,22 @@ public final class KeepAliveProvider { private volatile boolean ackThresholdBreached; private volatile long lastKeepAliveMillis; private volatile long lastAckMillis; - private final Publisher ticks; + private final Flux ticks; private final int keepAlivePeriodMillis; private final int missedKeepAliveThreshold; private final LongSupplier currentTimeSupplier; - private KeepAliveProvider(Publisher ticks, int keepAlivePeriodMillis, int missedKeepAliveThreshold, + private KeepAliveProvider(Flux ticks, int keepAlivePeriodMillis, int missedKeepAliveThreshold, LongSupplier currentTimeSupplier) { - this.ticks = s -> { - ticks.subscribe(new Subscriber() { - private Subscription subscription; - - @Override - public void onSubscribe(Subscription subscription) { - this.subscription = subscription; - s.onSubscribe(subscription); - } - - @Override - public void onNext(Long aLong) { - updateAckBreachThreshold(); - if (ackThresholdBreached) { - onError(new ConnectionException("Missing keep alive from the peer.")); - subscription.cancel(); - } else { - lastKeepAliveMillis = currentTimeSupplier.getAsLong(); - s.onNext(aLong); - } - } - - @Override - public void onError(Throwable t) { - s.onError(t); - } - - @Override - public void onComplete() { - s.onComplete(); - } - }); - }; + this.ticks = ticks.map(tick -> { + updateAckBreachThreshold(); + if (ackThresholdBreached) { + throw new ConnectionException("Missing keep alive from the peer."); + } else { + lastKeepAliveMillis = currentTimeSupplier.getAsLong(); + return tick; + } + }); this.keepAlivePeriodMillis = keepAlivePeriodMillis; this.missedKeepAliveThreshold = missedKeepAliveThreshold; this.currentTimeSupplier = currentTimeSupplier; @@ -87,7 +60,7 @@ public void onComplete() { * * @return Source of keep-alive ticks. */ - public Publisher ticks() { + public Flux ticks() { return ticks; } @@ -123,7 +96,7 @@ public int getMissedKeepAliveThreshold() { * @return A new {@link KeepAliveProvider} that never sends a keep-alive frame. */ public static KeepAliveProvider never() { - return from(Integer.MAX_VALUE, Px.never()); + return from(Integer.MAX_VALUE, Flux.never()); } /** @@ -135,7 +108,7 @@ public static KeepAliveProvider never() { * * @return A new {@link KeepAliveProvider} that never sends a keep-alive frame. */ - public static KeepAliveProvider from(int keepAlivePeriodMillis, Publisher keepAliveTicks) { + public static KeepAliveProvider from(int keepAlivePeriodMillis, Flux keepAliveTicks) { return from(keepAlivePeriodMillis, SetupProvider.DEFAULT_MAX_KEEP_ALIVE_MISSING_ACK, keepAliveTicks); } @@ -151,7 +124,7 @@ public static KeepAliveProvider from(int keepAlivePeriodMillis, Publisher * @return A new {@link KeepAliveProvider} that never sends a keep-alive frame. */ public static KeepAliveProvider from(int keepAlivePeriodMillis, int missedKeepAliveThreshold, - Publisher keepAliveTicks) { + Flux keepAliveTicks) { return from(keepAlivePeriodMillis, missedKeepAliveThreshold, keepAliveTicks, System::currentTimeMillis); } @@ -168,7 +141,7 @@ public static KeepAliveProvider from(int keepAlivePeriodMillis, int missedKeepAl * @return A new {@link KeepAliveProvider} that never sends a keep-alive frame. */ public static KeepAliveProvider from(int keepAlivePeriodMillis, int missedKeepAliveThreshold, - Publisher keepAliveTicks, LongSupplier currentTimeSupplier) { + Flux keepAliveTicks, LongSupplier currentTimeSupplier) { return new KeepAliveProvider(keepAliveTicks, keepAlivePeriodMillis, missedKeepAliveThreshold, currentTimeSupplier); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/ReactiveSocketClient.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/ReactiveSocketClient.java index 54bcc34e1..8c20d5c03 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/ReactiveSocketClient.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/ReactiveSocketClient.java @@ -24,7 +24,7 @@ import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.transport.TransportClient; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; public interface ReactiveSocketClient extends Availability, EventSource { @@ -33,7 +33,7 @@ public interface ReactiveSocketClient extends Availability, EventSource connect(); + Mono connect(); /** * Creates a new instances of {@code ReactiveSocketClient} using the passed {@code transportClient}. This client diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProvider.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProvider.java index 3d9bff325..5658d8692 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProvider.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProvider.java @@ -29,7 +29,7 @@ import io.reactivesocket.ReactiveSocket; import io.reactivesocket.frame.SetupFrameFlyweight; import io.reactivesocket.util.PayloadImpl; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; import java.util.function.Function; @@ -51,7 +51,7 @@ public interface SetupProvider extends EventSource { * * @return Asynchronous source for the created {@code ReactiveSocket} */ - Publisher accept(DuplexConnection connection, SocketAcceptor acceptor); + Mono accept(DuplexConnection connection, SocketAcceptor acceptor); /** * Creates a new {@code SetupProvider} by modifying the mime type for data payload of this {@code SetupProvider} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java index 61d1d5582..5012bbd2c 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java @@ -35,12 +35,9 @@ import io.reactivesocket.lease.LeaseHonoringSocket; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.StreamIdSupplier; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.publishers.InstrumentingPublisher; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; import io.reactivesocket.util.Clock; import io.reactivesocket.util.PayloadImpl; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; import java.util.function.Consumer; import java.util.function.Function; @@ -65,21 +62,21 @@ final class SetupProviderImpl extends AbstractEventSource i } @Override - public Publisher accept(DuplexConnection connection, SocketAcceptor acceptor) { - DuplexConnection dc; + public Mono accept(DuplexConnection connection, SocketAcceptor acceptor) { if (isEventPublishingEnabled()) { - dc = new ConnectionEventInterceptor(connection, this); + ConnectionEventInterceptor interceptor = new ConnectionEventInterceptor(connection, this); + Mono source = _setup(interceptor, acceptor); + return Mono.using( + () -> new ConnectInspector(this), + connectInspector -> source + .doOnSuccess(connectInspector::connectSuccess) + .doOnError(connectInspector::connectFailed) + .doOnCancel(connectInspector::connectCancelled), + connectInspector -> {} + ); } else { - dc = connection; + return _setup(connection, acceptor); } - - Publisher source = _setup(dc, acceptor); - return new InstrumentingPublisher<>(source, subscriber -> { - if (!isEventPublishingEnabled()) { - return ConnectInspector.empty; - } - return new ConnectInspector(this); - }, ConnectInspector::connectFailed, null, ConnectInspector::connectCancelled, ConnectInspector::connectSuccess); } @Override @@ -133,27 +130,26 @@ private Frame copySetupFrame() { return newSetup; } - private Publisher _setup(DuplexConnection connection, SocketAcceptor acceptor) { - return Px.from(connection.sendOne(copySetupFrame())) - .cast(ReactiveSocket.class) - .concatWith(Px.defer(() -> { - ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(connection); - ClientReactiveSocket sendingSocket = - new ClientReactiveSocket(multiplexer.asClientConnection(), errorConsumer, - StreamIdSupplier.clientSupplier(), - keepAliveProvider, this); - LeaseHonoringSocket leaseHonoringSocket = leaseDecorator.apply(sendingSocket); - - sendingSocket.start(leaseHonoringSocket); - - LeaseEnforcingSocket acceptingSocket = acceptor.accept(sendingSocket); - ServerReactiveSocket receivingSocket = new ServerReactiveSocket(multiplexer.asServerConnection(), - acceptingSocket, true, - errorConsumer, this); - receivingSocket.start(); - - return Px.just(leaseHonoringSocket); - })); + private Mono _setup(DuplexConnection connection, SocketAcceptor acceptor) { + return connection.sendOne(copySetupFrame()) + .then(() -> { + ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(connection); + ClientReactiveSocket sendingSocket = + new ClientReactiveSocket(multiplexer.asClientConnection(), errorConsumer, + StreamIdSupplier.clientSupplier(), + keepAliveProvider, this); + LeaseHonoringSocket leaseHonoringSocket = leaseDecorator.apply(sendingSocket); + + sendingSocket.start(leaseHonoringSocket); + + LeaseEnforcingSocket acceptingSocket = acceptor.accept(sendingSocket); + ServerReactiveSocket receivingSocket = new ServerReactiveSocket(multiplexer.asServerConnection(), + acceptingSocket, true, + errorConsumer, this); + receivingSocket.start(); + + return Mono.just(leaseHonoringSocket); + }); } private static class ConnectInspector { @@ -173,14 +169,15 @@ public ConnectInspector(EventPublisher publisher) { public void connectSuccess(ReactiveSocket socket) { if (publisher.isEventPublishingEnabled()) { publisher.getEventListener() - .connectCompleted(() -> socket.availability(), System.nanoTime() - startTime, NANOSECONDS); + .connectCompleted(socket::availability, System.nanoTime() - startTime, NANOSECONDS); socket.onClose() - .subscribe(Subscribers.doOnTerminate(() -> { - if (publisher.isEventPublishingEnabled()) { - publisher.getEventListener() - .socketClosed(Clock.elapsedSince(startTime), Clock.unit()); - } - })); + .doFinally(signalType -> { + if (publisher.isEventPublishingEnabled()) { + publisher.getEventListener() + .socketClosed(Clock.elapsedSince(startTime), Clock.unit()); + } + }) + .subscribe(); } } @@ -196,4 +193,4 @@ public void connectCancelled() { } } } -} +} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/ConnectionEventInterceptor.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/ConnectionEventInterceptor.java index 144472ba8..22e42842c 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/ConnectionEventInterceptor.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/events/ConnectionEventInterceptor.java @@ -15,17 +15,12 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; -import io.reactivesocket.events.EventListener.RequestType; import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import java.util.function.LongSupplier; - -import static java.util.concurrent.TimeUnit.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; public final class ConnectionEventInterceptor implements DuplexConnection { @@ -40,35 +35,18 @@ public ConnectionEventInterceptor(DuplexConnection delegate, EventPublisher send(Publisher frame) { - return delegate.send(Px.from(frame) - .map(f -> { - try { - publishEventsForFrameWrite(f); - } catch (Exception e) { - logger.info("Error while emitting events for frame " + f - + " written. Ignoring error.", e); - } - return f; - })); + public Mono send(Publisher frame) { + return delegate.send(Flux.from(frame).doOnNext(this::publishEventsForFrameWrite)); } @Override - public Publisher sendOne(Frame frame) { + public Mono sendOne(Frame frame) { return delegate.sendOne(frame); } @Override - public Publisher receive() { - return Px.from(delegate.receive()) - .map(f -> { - try { - publishEventsForFrameRead(f); - } catch (Exception e) { - logger.info("Error while emitting events for frame " + f + " read. Ignoring error.", e); - } - return f; - }); + public Flux receive() { + return delegate.receive().doOnNext(this::publishEventsForFrameRead); } @Override @@ -77,12 +55,12 @@ public double availability() { } @Override - public Publisher close() { + public Mono close() { return delegate.close(); } @Override - public Publisher onClose() { + public Mono onClose() { return delegate.onClose(); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocket.java index 5ddf66223..9ec014e87 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocket.java @@ -14,26 +14,34 @@ package io.reactivesocket.events; import io.reactivesocket.events.EventListener.RequestType; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; public interface EventPublishingSocket { EventPublishingSocket DISABLED = new EventPublishingSocket() { @Override - public Publisher decorateReceive(int streamId, Publisher stream, RequestType requestType) { + public Mono decorateReceive(int streamId, Mono stream, RequestType requestType) { return stream; } @Override - public Publisher decorateSend(int streamId, Publisher stream, long receiveStartTimeNanos, + public Flux decorateReceive(int streamId, Flux stream, RequestType requestType) { + return stream; + } + + @Override + public Mono decorateSend(int streamId, Mono stream, long receiveStartTimeNanos, RequestType requestType) { return stream; } }; - Publisher decorateReceive(int streamId, Publisher stream, RequestType requestType); + Mono decorateReceive(int streamId, Mono stream, RequestType requestType); + + Flux decorateReceive(int streamId, Flux stream, RequestType requestType); - Publisher decorateSend(int streamId, Publisher stream, long receiveStartTimeNanos, - RequestType requestType); + Mono decorateSend(int streamId, Mono stream, long receiveStartTimeNanos, + RequestType requestType); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocketImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocketImpl.java index dc0599b76..ba2dd3b9b 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocketImpl.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocketImpl.java @@ -15,9 +15,9 @@ import io.reactivesocket.events.EventListener.RequestType; import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.reactivestreams.extensions.internal.publishers.InstrumentingPublisher; import io.reactivesocket.util.Clock; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.concurrent.TimeUnit; @@ -32,22 +32,39 @@ public EventPublishingSocketImpl(EventPublisher eventPu } @Override - public Publisher decorateReceive(int streamId, Publisher stream, RequestType requestType) { - final long startTime = Clock.now(); - return new InstrumentingPublisher<>(stream, - subscriber -> new ReceiveInterceptor(streamId, requestType, startTime), - ReceiveInterceptor::receiveFailed, ReceiveInterceptor::receiveComplete, - ReceiveInterceptor::receiveCancelled, null); + public Mono decorateReceive(int streamId, Mono stream, RequestType requestType) { + return Mono.using( + () -> new ReceiveInterceptor(streamId, requestType, Clock.now()), + receiveInterceptor -> stream + .doOnSuccess(t -> receiveInterceptor.receiveComplete()) + .doOnError(receiveInterceptor::receiveFailed) + .doOnCancel(receiveInterceptor::receiveCancelled), + receiveInterceptor -> {} + ); } @Override - public Publisher decorateSend(int streamId, Publisher stream, long receiveStartTimeNanos, - RequestType requestType) { - return new InstrumentingPublisher<>(stream, - subscriber -> new SendInterceptor(streamId, requestType, - receiveStartTimeNanos), - SendInterceptor::sendFailed, SendInterceptor::sendComplete, - SendInterceptor::sendCancelled, null); + public Flux decorateReceive(int streamId, Flux stream, RequestType requestType) { + return Flux.using( + () -> new ReceiveInterceptor(streamId, requestType, Clock.now()), + receiveInterceptor -> stream + .doOnComplete(receiveInterceptor::receiveComplete) + .doOnError(receiveInterceptor::receiveFailed) + .doOnCancel(receiveInterceptor::receiveCancelled), + receiveInterceptor -> {} + ); + } + + @Override + public Mono decorateSend(int streamId, Mono stream, long receiveStartTimeNanos, RequestType requestType) { + return Mono.using( + () -> new SendInterceptor(streamId, requestType, receiveStartTimeNanos), + sendInterceptor -> stream + .doOnSuccess(t -> sendInterceptor.sendComplete()) + .doOnError(sendInterceptor::sendFailed) + .doOnCancel(sendInterceptor::sendCancelled), + sendInterceptor -> {} + ); } private class ReceiveInterceptor { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java index 5900719c6..cf6e5ea6e 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java @@ -18,11 +18,11 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; import org.agrona.BitUtil; import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; /** * {@link DuplexConnection#receive()} is a single stream on which the following type of frames arrive: @@ -36,179 +36,99 @@
  • Frames for streams initiated by the acceptor of the connection (server).
  • getServerInput() { - return sourceInput.evenStream(); - } - - /** - * Returns the frames for streams that were initiated by the client. - * - * @return The frames for streams that were initiated by the client. - */ - public Publisher getClientInput() { - return sourceInput.oddStream(); + public ClientServerInputMultiplexer(DuplexConnection source) { + final MonoProcessor> streamZero = MonoProcessor.create(); + final MonoProcessor> server = MonoProcessor.create(); + final MonoProcessor> client = MonoProcessor.create(); + + streamZeroConnection = new InternalDuplexConnection(source, streamZero); + serverConnection = new InternalDuplexConnection(source, server); + clientConnection = new InternalDuplexConnection(source, client); + + source.receive() + .groupBy(frame -> { + int streamId = frame.getStreamId(); + Type type; + if (streamId == 0) { + type = Type.ZERO; + } else if (BitUtil.isEven(streamId)) { + type = Type.SERVER; + } else { + type = Type.CLIENT; + } + return type; + }) + .subscribe(group -> { + switch (group.key()) { + case ZERO: + streamZero.onNext(group); + break; + case SERVER: + server.onNext(group); + break; + case CLIENT: + client.onNext(group); + break; + } + }); } public DuplexConnection asServerConnection() { - return new InternalDuplexConnection(getServerInput()); + return serverConnection; } public DuplexConnection asClientConnection() { - return new InternalDuplexConnection(getClientInput()); + return clientConnection; } - private static final class SourceInput implements Subscriber { + public DuplexConnection asStreamZeroConnection() { + return streamZeroConnection; + } + private static class InternalDuplexConnection implements DuplexConnection { private final DuplexConnection source; - private int subscriberCount; // Guarded by this - private volatile Subscription sourceSubscription; - private volatile ValidatingSubscription oddSubscription; - private volatile ValidatingSubscription evenSubscription; + private final MonoProcessor> processor; - public SourceInput(DuplexConnection source) { + public InternalDuplexConnection(DuplexConnection source, MonoProcessor> processor) { this.source = source; + this.processor = processor; } @Override - public void onSubscribe(Subscription s) { - boolean cancelThis; - synchronized (this) { - cancelThis = sourceSubscription != null/*ReactiveStreams rule 2.5*/; - sourceSubscription = s; - } - if (cancelThis) { - s.cancel(); - } else { - // Start downstream subscriptions only when this subscriber is active. This elimiates any buffering. - oddSubscription.getSubscriber().onSubscribe(oddSubscription); - evenSubscription.getSubscriber().onSubscribe(evenSubscription); - } + public Mono send(Publisher frame) { + return source.send(frame); } @Override - public void onNext(Frame frame) { - if (frame.getStreamId() == 0) { - evenSubscription.safeOnNext(frame); - oddSubscription.safeOnNext(frame); - } else if (BitUtil.isEven(frame.getStreamId())) { - evenSubscription.safeOnNext(frame); - } else { - oddSubscription.safeOnNext(frame); - } + public Mono sendOne(Frame frame) { + return source.sendOne(frame); } @Override - public void onError(Throwable t) { - oddSubscription.safeOnError(t); - evenSubscription.safeOnError(t); - } - - @Override - public void onComplete() { - oddSubscription.safeOnComplete(); - evenSubscription.safeOnComplete(); - } - - public Publisher oddStream() { - return s -> { - subscribe(s, true); - }; - } - - public Publisher evenStream() { - return s -> { - subscribe(s, false); - }; - } - - private void subscribe(Subscriber s, boolean odd) { - Throwable sendError = null; - boolean subscribeUp = false; - synchronized (this) { - if(subscriberCount == 0 || subscriberCount == 1) { - if (odd) { - if (oddSubscription == null) { - oddSubscription = newSubscription(s); - } else { - sendError = new IllegalStateException("An active subscription already exists."); - } - } else if (evenSubscription == null) { - evenSubscription = newSubscription(s); - } else { - sendError = new IllegalStateException("An active subscription already exists."); - } - subscriberCount++; - subscribeUp = subscriberCount == 2; - } else { - sendError = new IllegalStateException("More than " + 2 + " subscribers received."); - } - } - - if (sendError != null) { - s.onError(sendError); - } else if(subscribeUp) { - source.receive().subscribe(this); - } - } - - private ValidatingSubscription newSubscription(Subscriber s) { - return ValidatingSubscription.create(s, () -> { - final boolean cancelUp; - synchronized (this) { - cancelUp = --subscriberCount == 0; - } - if (cancelUp) { - sourceSubscription.cancel(); - } - }, requestN -> { - // Since these are requests from odd/even streams they are for different frames and hence can pass - // through to upstream. - sourceSubscription.request(requestN); - }); - } - } - - private class InternalDuplexConnection implements DuplexConnection { - - private final Publisher input; - public InternalDuplexConnection(Publisher input) { - this.input = input; + public Flux receive() { + return processor.flatMap(f -> f); } @Override - public Publisher send(Publisher frame) { - return sourceInput.source.send(frame); + public Mono close() { + return source.close(); } @Override - public Publisher receive() { - return input; + public Mono onClose() { + return source.onClose(); } @Override public double availability() { - return sourceInput.source.availability(); - } - - @Override - public Publisher close() { - return sourceInput.source.close(); - } - - @Override - public Publisher onClose() { - return sourceInput.source.onClose(); + return source.availability(); } } + } diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/FlowControlHelper.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/FlowControlHelper.java similarity index 97% rename from reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/FlowControlHelper.java rename to reactivesocket-core/src/main/java/io/reactivesocket/internal/FlowControlHelper.java index 2c382b008..c470c1596 100644 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/FlowControlHelper.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/FlowControlHelper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.reactivesocket.reactivestreams.extensions.internal; +package io.reactivesocket.internal; public final class FlowControlHelper { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/MonoOnErrorOrCancelReturn.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/MonoOnErrorOrCancelReturn.java new file mode 100644 index 000000000..fb50b881f --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/MonoOnErrorOrCancelReturn.java @@ -0,0 +1,126 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivesocket.internal; + +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import reactor.core.publisher.MonoSource; +import reactor.core.publisher.Operators; + +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + +public class MonoOnErrorOrCancelReturn extends MonoSource { + final Function onError; + final Supplier onCancel; + + public MonoOnErrorOrCancelReturn(Publisher source, Function onError, Supplier onCancel) { + super(source); + this.onError = Objects.requireNonNull(onError, "onError"); + this.onCancel = Objects.requireNonNull(onCancel, "onCancel"); + } + + @Override + public void subscribe(Subscriber s) { + source.subscribe(new OnErrorOrCancelReturnSubscriber(s, onError, onCancel)); + } + + static final class OnErrorOrCancelReturnSubscriber extends Operators.MonoSubscriber { + final Function onError; + final Supplier onCancel; + + Subscription s; + + int count; + + boolean done; + + public OnErrorOrCancelReturnSubscriber(Subscriber actual, Function onError, Supplier onCancel) { + super(actual); + this.onError = onError; + this.onCancel = onCancel; + } + + @Override + public void request(long n) { + super.request(n); + if (n > 0L) { + s.request(Long.MAX_VALUE); + } + } + + @Override + public void cancel() { + s.cancel(); + complete(onCancel.get()); + } + + @Override + public void onSubscribe(Subscription s) { + if (Operators.validate(this.s, s)) { + this.s = s; + actual.onSubscribe(this); + } + } + + @Override + @SuppressWarnings("unchecked") + public void onNext(T t) { + if (done) { + Operators.onNextDropped(t); + return; + } + value = t; + + if (++count > 1) { + cancel(); + + onError(new IndexOutOfBoundsException("Source emitted more than one item")); + } + } + + @Override + public void onError(Throwable t) { + if (done) { + Operators.onErrorDropped(t); + return; + } + done = true; + + complete(onError.apply(t)); + } + + @Override + public void onComplete() { + if (done) { + return; + } + done = true; + + int c = count; + if (c == 0) { + actual.onError(Operators.onOperatorError(this, + new NoSuchElementException("Source was empty"))); + } else if (c == 1) { + complete(value); + } + } + } +} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteReceiver.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteReceiver.java index c242374bf..d35fd3d37 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteReceiver.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteReceiver.java @@ -21,13 +21,10 @@ import io.reactivesocket.Payload; import io.reactivesocket.exceptions.ApplicationException; import io.reactivesocket.exceptions.CancelException; -import io.reactivesocket.reactivestreams.extensions.internal.FlowControlHelper; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; -import org.reactivestreams.Processor; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import reactor.core.publisher.FluxProcessor; /** * An abstraction to receive data from a {@link Publisher} that is available remotely over a {@code ReactiveSocket} @@ -51,7 +48,7 @@ * ready to write, no frames will be enqueued into the connection. All {@code RequestN} frames sent during such time * will be merged into a single {@code RequestN} frame. */ -public final class RemoteReceiver implements Processor { +public final class RemoteReceiver extends FluxProcessor { private final Publisher transportSource; private final DuplexConnection connection; @@ -129,7 +126,8 @@ public void subscribe(Subscriber s) { onNext(requestFrame); } connection.send(framesSource) - .subscribe(Subscribers.doOnError(throwable -> subscription.safeOnError(throwable))); + .doOnError(throwable -> subscription.safeOnError(throwable)) + .subscribe(); } @Override diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java index 1bf72402e..f50b6866d 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java @@ -20,8 +20,6 @@ import io.reactivesocket.Frame; import io.reactivesocket.Frame.RequestN; import io.reactivesocket.FrameType; -import io.reactivesocket.reactivestreams.extensions.internal.FlowControlHelper; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; import org.reactivestreams.Processor; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/ValidatingSubscription.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ValidatingSubscription.java similarity index 98% rename from reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/ValidatingSubscription.java rename to reactivesocket-core/src/main/java/io/reactivesocket/internal/ValidatingSubscription.java index e34c03057..9504f916f 100644 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/ValidatingSubscription.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ValidatingSubscription.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.reactivesocket.reactivestreams.extensions.internal; +package io.reactivesocket.internal; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseEnforcingSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseEnforcingSocket.java index 06cf0bb3e..5968e14a3 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseEnforcingSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseEnforcingSocket.java @@ -17,12 +17,9 @@ package io.reactivesocket.lease; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.exceptions.RejectedException; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.Cancellable; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; -import org.reactivestreams.Publisher; +import reactor.core.Disposable; +import reactor.core.publisher.Mono; import java.util.function.Consumer; import java.util.function.LongSupplier; @@ -31,16 +28,15 @@ public class DefaultLeaseEnforcingSocket extends DefaultLeaseHonoringSocket impl private final LeaseDistributor leaseDistributor; private volatile Consumer leaseSender; - private Cancellable distributorCancellation; - @SuppressWarnings("rawtypes") - private final Px rejectError; + private Disposable distributorCancellation; + private final Mono rejectError; public DefaultLeaseEnforcingSocket(ReactiveSocket delegate, LeaseDistributor leaseDistributor, LongSupplier currentTimeSupplier, boolean clientHonorsLeases) { super(delegate, currentTimeSupplier); this.leaseDistributor = leaseDistributor; if (!clientHonorsLeases) { - rejectError = Px.error(new RejectedException("Server overloaded.")); + rejectError = Mono.error(new RejectedException("Server overloaded.")); } else { rejectError = null; } @@ -58,8 +54,8 @@ public DefaultLeaseEnforcingSocket(ReactiveSocket delegate, LeaseDistributor lea @Override public void acceptLeaseSender(Consumer leaseSender) { this.leaseSender = leaseSender; - distributorCancellation = leaseDistributor.registerSocket(lease -> accept(lease)); - onClose().subscribe(Subscribers.doOnTerminate(() -> distributorCancellation.cancel())); + distributorCancellation = leaseDistributor.registerSocket(this); + onClose().doFinally(signalType -> distributorCancellation.dispose()).subscribe(); } @Override @@ -73,16 +69,16 @@ public LeaseDistributor getLeaseDistributor() { } @Override - public Publisher close() { - return Px.from(super.close()) + public Mono close() { + return super.close() .doOnSubscribe(subscription -> { leaseDistributor.shutdown(); }); } - @Override - protected Publisher rejectError() { - return null == rejectError ? super.rejectError() : rejectError; + @SuppressWarnings("unchecked") + protected Mono rejectError() { + return null == rejectError ? super.rejectError() : (Mono) rejectError; } /** @@ -97,12 +93,12 @@ public interface LeaseDistributor { /** * Registers a new socket (a consumer of lease) to this distributor. This registration can be canclled by - * cancelling the returned {@link Cancellable}. + * cancelling the returned {@link Disposable}. * * @param leaseConsumer Consumer of lease. * - * @return Cancellation handle. Call {@link Cancellable#cancel()} to unregister this socket. + * @return Cancellation handle. Call {@link Disposable#dispose()} to unregister this socket. */ - Cancellable registerSocket(Consumer leaseConsumer); + Disposable registerSocket(Consumer leaseConsumer); } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseHonoringSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseHonoringSocket.java index 4f466d26d..62d23221c 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseHonoringSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DefaultLeaseHonoringSocket.java @@ -19,30 +19,30 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.exceptions.RejectedException; -import io.reactivesocket.reactivestreams.extensions.Px; +import io.reactivesocket.util.ReactiveSocketProxy; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.LongSupplier; -public class DefaultLeaseHonoringSocket implements LeaseHonoringSocket { +public class DefaultLeaseHonoringSocket extends ReactiveSocketProxy implements LeaseHonoringSocket { private static final Logger logger = LoggerFactory.getLogger(DefaultLeaseHonoringSocket.class); private volatile Lease currentLease; - private final ReactiveSocket delegate; private final LongSupplier currentTimeSupplier; private final AtomicInteger remainingQuota; @SuppressWarnings("ThrowableInstanceNeverThrown") private static final RejectedException rejectedException = new RejectedException("Lease exhausted."); - @SuppressWarnings("rawtypes") - private static final Px rejectedPx = Px.error(rejectedException); + private static final Mono rejected = Mono.error(rejectedException); - public DefaultLeaseHonoringSocket(ReactiveSocket delegate, LongSupplier currentTimeSupplier) { - this.delegate = delegate; + public DefaultLeaseHonoringSocket(ReactiveSocket source, LongSupplier currentTimeSupplier) { + super(source); this.currentTimeSupplier = currentTimeSupplier; remainingQuota = new AtomicInteger(); } @@ -58,73 +58,63 @@ public void accept(Lease lease) { } @Override - public Publisher fireAndForget(Payload payload) { - return Px.defer(() -> { + public Mono fireAndForget(Payload payload) { + return Mono.defer(() -> { if (!checkLease()) { return rejectError(); } - return delegate.fireAndForget(payload); + return source.fireAndForget(payload); }); } @Override - public Publisher requestResponse(Payload payload) { - return Px.defer(() -> { + public Mono requestResponse(Payload payload) { + return Mono.defer(() -> { if (!checkLease()) { return rejectError(); } - return delegate.requestResponse(payload); + return source.requestResponse(payload); }); } @Override - public Publisher requestStream(Payload payload) { - return Px.defer(() -> { + public Flux requestStream(Payload payload) { + return Flux.defer(() -> { if (!checkLease()) { return rejectError(); } - return delegate.requestStream(payload); + return source.requestStream(payload); }); } @Override - public Publisher requestChannel(Publisher payloads) { - return Px.defer(() -> { + public Flux requestChannel(Publisher payloads) { + return Flux.defer(() -> { if (!checkLease()) { return rejectError(); } - return delegate.requestChannel(payloads); + return source.requestChannel(payloads); }); } @Override - public Publisher metadataPush(Payload payload) { - return Px.defer(() -> { + public Mono metadataPush(Payload payload) { + return Mono.defer(() -> { if (!checkLease()) { return rejectError(); } - return delegate.metadataPush(payload); + return source.metadataPush(payload); }); } @Override public double availability() { - return remainingQuota.get() <= 0 || currentLease.isExpired() ? 0.0 : delegate.availability(); - } - - @Override - public Publisher close() { - return delegate.close(); - } - - @Override - public Publisher onClose() { - return delegate.onClose(); + return remainingQuota.get() <= 0 || currentLease.isExpired() ? 0.0 : source.availability(); } @SuppressWarnings("unchecked") - protected Publisher rejectError() { - return rejectedPx; + protected Mono rejectError() { + return (Mono) rejected; } private boolean checkLease() { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisableLeaseSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisableLeaseSocket.java index 9cbbf9c38..5ccf3b111 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisableLeaseSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisableLeaseSocket.java @@ -16,67 +16,24 @@ package io.reactivesocket.lease; -import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import org.reactivestreams.Publisher; +import io.reactivesocket.util.ReactiveSocketProxy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link LeaseHonoringSocket} that does not expect to receive any leases and {@link #accept(Lease)} throws an error. */ -public class DisableLeaseSocket implements LeaseHonoringSocket { +public class DisableLeaseSocket extends ReactiveSocketProxy implements LeaseHonoringSocket { private static final Logger logger = LoggerFactory.getLogger(DisableLeaseSocket.class); - private final ReactiveSocket delegate; - - public DisableLeaseSocket(ReactiveSocket delegate) { - this.delegate = delegate; + public DisableLeaseSocket(ReactiveSocket source) { + super(source); } @Override public void accept(Lease lease) { logger.info("Leases are disabled but received a lease from the peer. " + lease); } - - @Override - public Publisher fireAndForget(Payload payload) { - return delegate.fireAndForget(payload); - } - - @Override - public Publisher requestResponse(Payload payload) { - return delegate.requestResponse(payload); - } - - @Override - public Publisher requestStream(Payload payload) { - return delegate.requestStream(payload); - } - - @Override - public Publisher requestChannel(Publisher payloads) { - return delegate.requestChannel(payloads); - } - - @Override - public Publisher metadataPush(Payload payload) { - return delegate.metadataPush(payload); - } - - @Override - public double availability() { - return delegate.availability(); - } - - @Override - public Publisher close() { - return delegate.close(); - } - - @Override - public Publisher onClose() { - return delegate.onClose(); - } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocket.java index 2db2f81af..beebc9477 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocket.java @@ -18,61 +18,19 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.util.ReactiveSocketProxy; import org.reactivestreams.Publisher; import java.util.function.Consumer; -public final class DisabledLeaseAcceptingSocket implements LeaseEnforcingSocket { +public final class DisabledLeaseAcceptingSocket extends ReactiveSocketProxy implements LeaseEnforcingSocket { - private final ReactiveSocket delegate; - - public DisabledLeaseAcceptingSocket(ReactiveSocket delegate) { - this.delegate = delegate; + public DisabledLeaseAcceptingSocket(ReactiveSocket source) { + super(source); } @Override public void acceptLeaseSender(Consumer leaseSender) { // No Op, shouldn't be used when leases are required. } - - @Override - public Publisher fireAndForget(Payload payload) { - return delegate.fireAndForget(payload); - } - - @Override - public Publisher requestResponse(Payload payload) { - return delegate.requestResponse(payload); - } - - @Override - public Publisher requestStream(Payload payload) { - return delegate.requestStream(payload); - } - - @Override - public Publisher requestChannel( - Publisher payloads) { - return delegate.requestChannel(payloads); - } - - @Override - public Publisher metadataPush(Payload payload) { - return delegate.metadataPush(payload); - } - - @Override - public double availability() { - return delegate.availability(); - } - - @Override - public Publisher close() { - return delegate.close(); - } - - @Override - public Publisher onClose() { - return delegate.onClose(); - } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/FairLeaseDistributor.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/FairLeaseDistributor.java index e0238e106..bb62e30cb 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/FairLeaseDistributor.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/FairLeaseDistributor.java @@ -18,11 +18,9 @@ import io.reactivesocket.Frame; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.Cancellable; -import io.reactivesocket.reactivestreams.extensions.internal.CancellableImpl; -import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; import java.util.concurrent.LinkedBlockingQueue; import java.util.function.Consumer; @@ -39,11 +37,11 @@ public final class FairLeaseDistributor implements DefaultLeaseEnforcingSocket.L private volatile boolean startTicks; private final IntSupplier capacitySupplier; private final int leaseTTLMillis; - private final Publisher leaseDistributionTicks; + private final Flux leaseDistributionTicks; private final boolean redistributeOnConnect; public FairLeaseDistributor(IntSupplier capacitySupplier, int leaseTTLMillis, - Publisher leaseDistributionTicks, boolean redistributeOnConnect) { + Flux leaseDistributionTicks, boolean redistributeOnConnect) { this.capacitySupplier = capacitySupplier; /* * If lease TTL is exactly the same as the period of replenishment, then there would be a time period when new @@ -59,7 +57,7 @@ public FairLeaseDistributor(IntSupplier capacitySupplier, int leaseTTLMillis, } public FairLeaseDistributor(IntSupplier capacitySupplier, int leaseTTLMillis, - Publisher leaseDistributionTicks) { + Flux leaseDistributionTicks) { this(capacitySupplier, leaseTTLMillis, leaseDistributionTicks, true); } @@ -80,7 +78,7 @@ public void shutdown() { * @return A handle to cancel this registration, when the socket is closed. */ @Override - public Cancellable registerSocket(Consumer leaseConsumer) { + public Disposable registerSocket(Consumer leaseConsumer) { activeRecipients.add(leaseConsumer); boolean _started; synchronized (this) { @@ -99,12 +97,7 @@ public Cancellable registerSocket(Consumer leaseConsumer) { distribute(capacitySupplier.getAsInt()); } - return new CancellableImpl() { - @Override - protected void onCancel() { - activeRecipients.remove(leaseConsumer); - } - }; + return () -> activeRecipients.remove(leaseConsumer); } private void distribute(int permits) { @@ -130,12 +123,11 @@ private void distribute(int permits) { } private void startTicks() { - Px.from(leaseDistributionTicks) + leaseDistributionTicks .doOnSubscribe(subscription -> ticksSubscription = subscription) .doOnNext(aLong -> { distribute(capacitySupplier.getAsInt()); }) - .ignore() .subscribe(); } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/NullLeaseGovernor.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/NullLeaseGovernor.java deleted file mode 100644 index 2d68f6396..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/NullLeaseGovernor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.lease; - -import io.reactivesocket.LeaseGovernor; - -public class NullLeaseGovernor implements LeaseGovernor { - /* - @Override - public void register(Responder responder) {} - - @Override - public void unregister(Responder responder) {} - - @Override - public boolean accept(Responder responder, Frame frame) { - return true; - } - */ -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/UnlimitedLeaseGovernor.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/UnlimitedLeaseGovernor.java deleted file mode 100644 index 98853dc19..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/UnlimitedLeaseGovernor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.lease; - -import io.reactivesocket.LeaseGovernor; - -public class UnlimitedLeaseGovernor implements LeaseGovernor { - /* - @Override - public void register(Responder responder) { - responder.sendLease(Integer.MAX_VALUE, Integer.MAX_VALUE); - } - - @Override - public void unregister(Responder responder) {} - - @Override - public boolean accept(Responder responder, Frame frame) { - return true; - } - */ -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java b/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java index 67f116d22..2a7d33977 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java @@ -27,11 +27,10 @@ import io.reactivesocket.lease.DefaultLeaseHonoringSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.lease.LeaseHonoringSocket; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; import io.reactivesocket.transport.TransportServer; import io.reactivesocket.transport.TransportServer.StartedServer; import io.reactivesocket.util.Clock; +import reactor.core.publisher.Mono; public final class DefaultReactiveSocketServer extends AbstractEventSource implements ReactiveSocketServer { @@ -50,42 +49,45 @@ public StartedServer start(SocketAcceptor acceptor) { long startTime = Clock.now(); dc = new ConnectionEventInterceptor(connection, this); getEventListener().socketAccepted(); - dc.onClose() - .subscribe(Subscribers.doOnTerminate(() -> { - if (isEventPublishingEnabled()) { - getEventListener().socketClosed(Clock.elapsedSince(startTime), Clock.unit()); - } - })); + dc.onClose().doFinally(signalType -> { + if (isEventPublishingEnabled()) { + getEventListener().socketClosed(Clock.elapsedSince(startTime), Clock.unit()); + } + }).subscribe(); } else { dc = connection; } - return Px.from(dc.receive()) - .switchTo(setupFrame -> { - if (setupFrame.getType() == FrameType.SETUP) { - ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(dc); - ConnectionSetupPayload setup = ConnectionSetupPayload.create(setupFrame); - ClientReactiveSocket sender = new ClientReactiveSocket(multiplexer.asServerConnection(), - Throwable::printStackTrace, - StreamIdSupplier.serverSupplier(), - KeepAliveProvider.never(), - this); - LeaseHonoringSocket lhs = new DefaultLeaseHonoringSocket(sender); - sender.start(lhs); - LeaseEnforcingSocket handler = acceptor.accept(setup, sender); - ServerReactiveSocket receiver = new ServerReactiveSocket(multiplexer.asClientConnection(), - handler, - setup.willClientHonorLease(), - Throwable::printStackTrace, - this); - receiver.start(); - return dc.onClose(); - } else { - return Px.error(new IllegalStateException("Invalid first frame on the connection: " - + dc + ", frame type received: " - + setupFrame.getType())); - } - }); + ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(dc); + + return multiplexer + .asStreamZeroConnection() + .receive() + .next() + .then(setupFrame -> { + if (setupFrame.getType() == FrameType.SETUP) { + ConnectionSetupPayload setup = ConnectionSetupPayload.create(setupFrame); + ClientReactiveSocket sender = new ClientReactiveSocket(multiplexer.asServerConnection(), + Throwable::printStackTrace, + StreamIdSupplier.serverSupplier(), + KeepAliveProvider.never(), + this); + LeaseHonoringSocket lhs = new DefaultLeaseHonoringSocket(sender); + sender.start(lhs); + LeaseEnforcingSocket handler = acceptor.accept(setup, sender); + ServerReactiveSocket receiver = new ServerReactiveSocket(multiplexer.asClientConnection(), + handler, + setup.willClientHonorLease(), + Throwable::printStackTrace, + this); + receiver.start(); + return dc.onClose(); + } else { + return Mono.error(new IllegalStateException("Invalid first frame on the connection: " + + dc + ", frame type received: " + + setupFrame.getType())); + } + }); }); } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/transport/TransportClient.java b/reactivesocket-core/src/main/java/io/reactivesocket/transport/TransportClient.java index 70e52478c..0bfd698db 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/transport/TransportClient.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/transport/TransportClient.java @@ -17,7 +17,7 @@ package io.reactivesocket.transport; import io.reactivesocket.DuplexConnection; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; import java.net.SocketAddress; @@ -31,6 +31,6 @@ public interface TransportClient { * * @return {@code Publisher}, every subscription returns a single {@code DuplexConnection}. */ - Publisher connect(); + Mono connect(); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/transport/TransportServer.java b/reactivesocket-core/src/main/java/io/reactivesocket/transport/TransportServer.java index 93836eb83..87842a5aa 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/transport/TransportServer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/transport/TransportServer.java @@ -18,6 +18,7 @@ import io.reactivesocket.DuplexConnection; import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; import java.net.SocketAddress; import java.util.concurrent.TimeUnit; @@ -51,7 +52,7 @@ interface ConnectionAcceptor extends Function> * @return A {@code Publisher} which terminates when the processing of the connection finishes. */ @Override - Publisher apply(DuplexConnection duplexConnection); + Mono apply(DuplexConnection duplexConnection); } /** diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketDecorator.java b/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketDecorator.java deleted file mode 100644 index a6ca699ec..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketDecorator.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.util; - -import io.reactivesocket.AbstractReactiveSocket; -import io.reactivesocket.Payload; -import io.reactivesocket.ReactiveSocket; -import org.reactivestreams.Publisher; - -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * A utility class to decorate parts of the API of an existing {@link ReactiveSocket}.

    - * All methods mutate state, hence, this class is not thread-safe. - */ -public class ReactiveSocketDecorator { - - private Function> reqResp; - private Function> reqStream; - private Function, Publisher> reqChannel; - private Function> fnf; - private Function> metaPush; - private Supplier availability; - private Supplier> close; - private Supplier> onClose; - - private final ReactiveSocket delegate; - - private ReactiveSocketDecorator(ReactiveSocket delegate) { - this.delegate = delegate; - reqResp = payload -> delegate.requestResponse(payload); - reqStream = payload -> delegate.requestStream(payload); - reqChannel = payload -> delegate.requestChannel(payload); - fnf = payload -> delegate.fireAndForget(payload); - metaPush = payload -> delegate.metadataPush(payload); - availability = () -> delegate.availability(); - close = () -> delegate.close(); - onClose = () -> delegate.onClose(); - } - - public ReactiveSocket finish() { - return new ReactiveSocket() { - @Override - public Publisher fireAndForget(Payload payload) { - return fnf.apply(payload); - } - - @Override - public Publisher requestResponse(Payload payload) { - return reqResp.apply(payload); - } - - @Override - public Publisher requestStream(Payload payload) { - return reqStream.apply(payload); - } - - @Override - public Publisher requestChannel(Publisher payloads) { - return reqChannel.apply(payloads); - } - - @Override - public Publisher metadataPush(Payload payload) { - return metaPush.apply(payload); - } - - @Override - public Publisher close() { - return close.get(); - } - - @Override - public Publisher onClose() { - return onClose.get(); - } - - @Override - public double availability() { - return availability.get(); - } - }; - } - - /** - * Decorates underlying {@link ReactiveSocket#requestResponse(Payload)} with the provided mapping function. - * - * @param responseMapper Mapper used to decorate the response of {@link ReactiveSocket#requestResponse(Payload)}. - * Input to the function is the original response of the underlying {@code ReactiveSocket} - * - * @return {@code this} - */ - public ReactiveSocketDecorator requestResponse(Function, Publisher> responseMapper) { - reqResp = payload -> responseMapper.apply(delegate.requestResponse(payload)); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#requestResponse(Payload)} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. First argument here is - * the payload for request and the socket passed is the underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator requestResponse(BiFunction> mapper) { - reqResp = payload -> mapper.apply(payload, delegate); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#requestStream(Payload)} with the provided mapping function. - * - * @param responseMapper Mapper used to decorate the response of {@link ReactiveSocket#requestStream(Payload)}. - * Input to the function is the original response of the underlying {@code ReactiveSocket} - * - * @return {@code this} - */ - public ReactiveSocketDecorator requestStream(Function, Publisher> responseMapper) { - reqStream = payload -> responseMapper.apply(delegate.requestStream(payload)); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#requestStream(Payload)} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. First argument here is - * the payload for request and the socket passed is the underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator requestStream(BiFunction> mapper) { - reqStream = payload -> mapper.apply(payload, delegate); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#requestChannel(Publisher)} with the provided mapping function. - * - * @param responseMapper Mapper used to decorate the response of {@link ReactiveSocket#requestChannel(Publisher)}. - * Input to the function is the original response of the underlying {@code ReactiveSocket} - * - * @return {@code this} - */ - public ReactiveSocketDecorator requestChannel(Function, Publisher> responseMapper) { - reqChannel = payload -> responseMapper.apply(delegate.requestChannel(payload)); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#requestChannel(Publisher)} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. First argument here is - * the payload for request and the socket passed is the underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator requestChannel(BiFunction, ReactiveSocket, Publisher> mapper) { - reqChannel = payloads -> mapper.apply(payloads, delegate); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#fireAndForget(Payload)} with the provided mapping function. - * - * @param responseMapper Mapper used to decorate the response of {@link ReactiveSocket#fireAndForget(Payload)}. - * Input to the function is the original response of the underlying {@code ReactiveSocket} - * - * @return {@code this} - */ - public ReactiveSocketDecorator fireAndForget(Function, Publisher> responseMapper) { - fnf = payload -> responseMapper.apply(delegate.fireAndForget(payload)); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#fireAndForget(Payload)} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. First argument here is - * the payload for request and the socket passed is the underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator fireAndForget(BiFunction> mapper) { - fnf = payloads -> mapper.apply(payloads, delegate); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#metadataPush(Payload)} with the provided mapping function. - * - * @param responseMapper Mapper used to decorate the response of {@link ReactiveSocket#metadataPush(Payload)}. - * Input to the function is the original response of the underlying {@code ReactiveSocket} - * - * @return {@code this} - */ - public ReactiveSocketDecorator metadataPush(Function, Publisher> responseMapper) { - metaPush = payload -> responseMapper.apply(delegate.metadataPush(payload)); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#metadataPush(Payload)} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. First argument here is - * the payload for request and the socket passed is the underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator metadataPush(BiFunction> mapper) { - metaPush = payloads -> mapper.apply(payloads, delegate); - return this; - } - - /** - * Decorates all responses of the underlying {@code ReactiveSocket} with the provided mapping function. This will - * only decorate {@link ReactiveSocket#requestResponse(Payload)}, {@link ReactiveSocket#requestStream(Payload)} - * and {@link ReactiveSocket#requestChannel(Publisher)}. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. First argument here is - * the original response. - * - * @return {@code this} - */ - public ReactiveSocketDecorator decorateAllResponses(Function, Publisher> mapper) { - requestResponse(resp -> mapper.apply(resp)).requestStream(resp -> mapper.apply(resp)) - .requestChannel(resp -> mapper.apply(resp)); - return this; - } - - /** - * Decorates all responses of the underlying {@code ReactiveSocket} with the provided mapping function. This will - * only decorate {@link ReactiveSocket#metadataPush(Payload)} and {@link ReactiveSocket#fireAndForget(Payload)}. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. First argument here is - * the original response. - * - * @return {@code this} - */ - public ReactiveSocketDecorator decorateAllVoidResponses(Function, Publisher> mapper) { - fireAndForget(resp -> mapper.apply(resp)).metadataPush(resp -> mapper.apply(resp)); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#close()} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. Argument here is the - * underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator close(Function> mapper) { - close = () -> mapper.apply(delegate); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#onClose()} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. Argument here is the - * underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator onClose(Function> mapper) { - onClose = () -> mapper.apply(delegate); - return this; - } - - /** - * Decorates underlying {@link ReactiveSocket#availability()} with the provided mapping function. - * - * @param mapper Mapper used to override the call to the underlying {@code ReactiveSocket}. Argument here is the - * underlying {@code ReactiveSocket}. - * - * @return {@code this} - */ - public ReactiveSocketDecorator availability(Function mapper) { - availability = () -> mapper.apply(delegate); - return this; - } - - /** - * Starts wrapping the passed {@code source}. Use instance level methods to decorate the APIs. - * - * @param source Source socket to decorate. - * - * @return A new {@code ReactiveSocketDecorator} instance. - */ - public static ReactiveSocketDecorator wrap(ReactiveSocket source) { - return new ReactiveSocketDecorator(source); - } - - /** - * Starts with a {@code ReactiveSocket} that rejects all requests. Use instance level methods to decorate the APIs. - * - * @return A new {@code ReactiveSocketDecorator} instance. - */ - public static ReactiveSocketDecorator empty() { - return new ReactiveSocketDecorator(new AbstractReactiveSocket() { }); - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketProxy.java b/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketProxy.java index 8f97c3b73..031d7c2f8 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketProxy.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/util/ReactiveSocketProxy.java @@ -18,91 +18,57 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; - -import java.util.function.Function; - +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; /** * Wrapper/Proxy for a ReactiveSocket. * This is useful when we want to override a specific method. */ public class ReactiveSocketProxy implements ReactiveSocket { - protected final ReactiveSocket child; - private final Function, Subscriber> subscriberWrapper; - - public ReactiveSocketProxy(ReactiveSocket child, Function, Subscriber> subscriberWrapper) { - this.child = child; - this.subscriberWrapper = subscriberWrapper; - } + protected final ReactiveSocket source; - public ReactiveSocketProxy(ReactiveSocket child) { - this(child, null); + public ReactiveSocketProxy(ReactiveSocket source) { + this.source = source; } @Override - public Publisher fireAndForget(Payload payload) { - return child.fireAndForget(payload); + public Mono fireAndForget(Payload payload) { + return source.fireAndForget(payload); } @Override - public Publisher requestResponse(Payload payload) { - if (subscriberWrapper == null) { - return child.requestResponse(payload); - } else { - return s -> { - Subscriber subscriber = subscriberWrapper.apply(s); - child.requestResponse(payload).subscribe(subscriber); - }; - } + public Mono requestResponse(Payload payload) { + return source.requestResponse(payload); } @Override - public Publisher requestStream(Payload payload) { - if (subscriberWrapper == null) { - return child.requestStream(payload); - } else { - return s -> { - Subscriber subscriber = subscriberWrapper.apply(s); - child.requestStream(payload).subscribe(subscriber); - }; - } + public Flux requestStream(Payload payload) { + return source.requestStream(payload); } @Override - public Publisher requestChannel(Publisher payloads) { - if (subscriberWrapper == null) { - return child.requestChannel(payloads); - } else { - return s -> { - Subscriber subscriber = subscriberWrapper.apply(s); - child.requestChannel(payloads).subscribe(subscriber); - }; - } + public Flux requestChannel(Publisher payloads) { + return source.requestChannel(payloads); } @Override - public Publisher metadataPush(Payload payload) { - return child.metadataPush(payload); + public Mono metadataPush(Payload payload) { + return source.metadataPush(payload); } @Override public double availability() { - return child.availability(); - } - - @Override - public Publisher close() { - return child.close(); + return source.availability(); } @Override - public Publisher onClose() { - return child.onClose(); + public Mono close() { + return source.close(); } @Override - public String toString() { - return "ReactiveSocketProxy(" + child + ')'; + public Mono onClose() { + return source.onClose(); } } \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java index d54636cb8..19ab39b5e 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/ClientReactiveSocketTest.java @@ -20,12 +20,12 @@ import io.reactivesocket.exceptions.ApplicationException; import io.reactivesocket.exceptions.RejectedSetupException; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import io.reactivex.processors.PublishProcessor; import org.junit.Rule; import org.junit.Test; import org.reactivestreams.Publisher; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; @@ -52,9 +52,11 @@ public void testInvalidFrameOnStream0() throws Throwable { assertThat("Unexpected error received.", rule.errors, contains(instanceOf(IllegalStateException.class))); } - @Test(timeout = 2_000, expected = RejectedSetupException.class) + @Test(timeout = 2_000) public void testHandleSetupException() throws Throwable { rule.connection.addToReceivedBuffer(Frame.Error.from(0, new RejectedSetupException("boom"))); + assertThat("Unexpected errors.", rule.errors, hasSize(1)); + assertThat("Unexpected error received.", rule.errors, contains(instanceOf(RejectedSetupException.class))); } @Test(timeout = 2_000) @@ -101,10 +103,9 @@ public void testRequestReplyWithCancel() throws Throwable { @Test(timeout = 2_000) public void testRequestReplyErrorOnSend() throws Throwable { rule.connection.setAvailability(0); // Fails send - Publisher response = rule.socket.requestResponse(PayloadImpl.EMPTY); + Mono response = rule.socket.requestResponse(PayloadImpl.EMPTY); TestSubscriber responseSub = TestSubscriber.create(); - Flowable.fromPublisher(response) - .subscribe(responseSub); + response.subscribe(responseSub); responseSub.assertError(RuntimeException.class); } @@ -129,7 +130,7 @@ public int sendRequestResponse(Publisher response) { public static class ClientSocketRule extends AbstractSocketRule { - private final PublishProcessor keepAliveTicks = PublishProcessor.create(); + private final DirectProcessor keepAliveTicks = DirectProcessor.create(); @Override protected ClientReactiveSocket newReactiveSocket() { diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/ReactiveSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/ReactiveSocketTest.java index a2f5be518..08f50ae96 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/ReactiveSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/ReactiveSocketTest.java @@ -18,19 +18,17 @@ import io.reactivesocket.client.KeepAliveProvider; import io.reactivesocket.exceptions.InvalidRequestException; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.test.util.LocalDuplexConnection; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import io.reactivex.processors.PublishProcessor; import org.hamcrest.MatcherAssert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.runner.Description; import org.junit.runners.model.Statement; -import org.reactivestreams.Publisher; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Mono; import java.util.ArrayList; @@ -46,7 +44,7 @@ public class ReactiveSocketTest { @Test(timeout = 2_000) public void testRequestReplyNoError() { TestSubscriber subscriber = TestSubscriber.create(); - Flowable.fromPublisher(rule.crs.requestResponse(new PayloadImpl("hello"))) + rule.crs.requestResponse(new PayloadImpl("hello")) .subscribe(subscriber); await(subscriber).assertNoErrors().assertComplete().assertValueCount(1); rule.assertNoErrors(); @@ -56,12 +54,12 @@ public void testRequestReplyNoError() { public void testHandlerEmitsError() { rule.setRequestAcceptor(new AbstractReactiveSocket() { @Override - public Publisher requestResponse(Payload payload) { - return Flowable.error(new NullPointerException("Deliberate exception.")); + public Mono requestResponse(Payload payload) { + return Mono.error(new NullPointerException("Deliberate exception.")); } }); TestSubscriber subscriber = TestSubscriber.create(); - Flowable.fromPublisher(rule.crs.requestResponse(PayloadImpl.EMPTY)) + rule.crs.requestResponse(PayloadImpl.EMPTY) .subscribe(subscriber); await(subscriber).assertNotComplete().assertNoValues() .assertError(InvalidRequestException.class); @@ -82,8 +80,8 @@ public static class SocketRule extends ExternalResource { private ClientReactiveSocket crs; private ServerReactiveSocket srs; private ReactiveSocket requestAcceptor; - PublishProcessor serverProcessor; - PublishProcessor clientProcessor; + DirectProcessor serverProcessor; + DirectProcessor clientProcessor; private ArrayList clientErrors = new ArrayList<>(); private ArrayList serverErrors = new ArrayList<>(); @@ -99,16 +97,16 @@ public void evaluate() throws Throwable { } protected void init() { - serverProcessor = PublishProcessor.create(); - clientProcessor = PublishProcessor.create(); + serverProcessor = DirectProcessor.create(); + clientProcessor = DirectProcessor.create(); LocalDuplexConnection serverConnection = new LocalDuplexConnection("server", clientProcessor, serverProcessor); LocalDuplexConnection clientConnection = new LocalDuplexConnection("client", serverProcessor, clientProcessor); requestAcceptor = null != requestAcceptor? requestAcceptor : new AbstractReactiveSocket() { @Override - public Publisher requestResponse(Payload payload) { - return Px.just(payload); + public Mono requestResponse(Payload payload) { + return Mono.just(payload); } }; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java index bb15ac115..1775c035e 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java @@ -16,13 +16,12 @@ package io.reactivesocket; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.test.util.TestDuplexConnection; import io.reactivesocket.util.PayloadImpl; import org.junit.Rule; import org.junit.Test; -import org.reactivestreams.Publisher; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.Mono; import java.util.Collection; import java.util.concurrent.ConcurrentLinkedQueue; @@ -76,8 +75,8 @@ public void testCancel() throws Exception { final AtomicBoolean cancelled = new AtomicBoolean(); rule.setAcceptingSocket(new AbstractReactiveSocket() { @Override - public Publisher requestResponse(Payload payload) { - return Px.never() + public Mono requestResponse(Payload payload) { + return Mono.never() .doOnCancel(() -> cancelled.set(true)); } }); @@ -99,8 +98,8 @@ public static class ServerSocketRule extends AbstractSocketRule requestResponse(Payload payload) { - return Px.just(payload); + public Mono requestResponse(Payload payload) { + return Mono.just(payload); } }; super.init(); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/StreamIdSupplierTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/StreamIdSupplierTest.java index 43b504175..caf32dd5c 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/StreamIdSupplierTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/StreamIdSupplierTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package io.reactivesocket; import org.junit.Test; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/client/KeepAliveProviderTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/client/KeepAliveProviderTest.java index bfc96cfc0..468d652fe 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/client/KeepAliveProviderTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/client/KeepAliveProviderTest.java @@ -17,9 +17,9 @@ package io.reactivesocket.client; import io.reactivesocket.exceptions.ConnectionException; -import io.reactivex.Flowable; import io.reactivex.subscribers.TestSubscriber; import org.junit.Test; +import reactor.core.publisher.Flux; import java.util.concurrent.atomic.AtomicLong; @@ -27,7 +27,7 @@ public class KeepAliveProviderTest { @Test public void testEmptyTicks() throws Exception { - KeepAliveProvider provider = KeepAliveProvider.from(10, 1, Flowable.empty(), () -> 1); + KeepAliveProvider provider = KeepAliveProvider.from(10, 1, Flux.empty(), () -> 1); TestSubscriber subscriber = TestSubscriber.create(); provider.ticks().subscribe(subscriber); subscriber.assertComplete().assertNoErrors().assertNoValues(); @@ -36,18 +36,18 @@ public void testEmptyTicks() throws Exception { @Test public void testTicksWithAck() throws Exception { AtomicLong time = new AtomicLong(); - KeepAliveProvider provider = KeepAliveProvider.from(10, 1, Flowable.just(1L, 2L), () -> time.longValue()); + KeepAliveProvider provider = KeepAliveProvider.from(10, 1, Flux.just(1L, 2L), time::longValue); TestSubscriber subscriber = TestSubscriber.create(); - Flowable.fromPublisher(provider.ticks()).doOnNext(aLong -> provider.ack()).subscribe(subscriber); + provider.ticks().doOnNext(aLong -> provider.ack()).subscribe(subscriber); subscriber.assertNoErrors().assertComplete().assertValues(1L, 2L); } @Test public void testMissingAck() throws Exception { AtomicLong time = new AtomicLong(); - KeepAliveProvider provider = KeepAliveProvider.from(10, 1, Flowable.just(1L, 2L), () -> time.addAndGet(100)); + KeepAliveProvider provider = KeepAliveProvider.from(10, 1, Flux.just(1L, 2L), () -> time.addAndGet(100)); TestSubscriber subscriber = TestSubscriber.create(); - Flowable.fromPublisher(provider.ticks()).subscribe(subscriber); + provider.ticks().subscribe(subscriber); subscriber.assertError(ConnectionException.class).assertValues(1L); } } \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/client/SetupProviderImplTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/client/SetupProviderImplTest.java index bd9b92556..b332976ec 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/client/SetupProviderImplTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/client/SetupProviderImplTest.java @@ -25,8 +25,9 @@ import io.reactivesocket.lease.FairLeaseDistributor; import io.reactivesocket.test.util.TestDuplexConnection; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; import org.junit.Test; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -49,13 +50,11 @@ public void testSetup() throws Exception { setupProvider = setupProvider.setupPayload(setupPayload); TestDuplexConnection connection = new TestDuplexConnection(); - FairLeaseDistributor distributor = new FairLeaseDistributor(() -> 0, 0, Flowable.never()); - ReactiveSocket socket = Flowable.fromPublisher(setupProvider - .accept(connection, - reactiveSocket -> new DefaultLeaseEnforcingSocket( - reactiveSocket, distributor))) - .switchIfEmpty(Flowable.error(new IllegalStateException("No socket returned."))) - .blockingFirst(); + FairLeaseDistributor distributor = new FairLeaseDistributor(() -> 0, 0, Flux.never()); + ReactiveSocket socket = setupProvider + .accept(connection, reactiveSocket -> new DefaultLeaseEnforcingSocket(reactiveSocket, distributor)) + .otherwiseIfEmpty(Mono.error(new IllegalStateException("No socket returned."))) + .block(); dataBuffer.rewind(); metaDataBuffer.rewind(); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java index 83c8c61d8..aec5bcdfe 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java @@ -21,8 +21,8 @@ import io.reactivesocket.TestUtil; import io.reactivesocket.frame.FrameHeaderFlyweight; import io.reactivesocket.frame.PayloadReassembler; -import io.reactivex.processors.ReplayProcessor; import org.junit.Test; +import reactor.core.publisher.ReplayProcessor; import java.nio.ByteBuffer; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java index 1f174f9f4..7cd5bd8cf 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java @@ -23,13 +23,13 @@ import io.reactivesocket.exceptions.CancelException; import io.reactivesocket.test.util.TestDuplexConnection; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.processors.UnicastProcessor; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.runner.Description; import org.junit.runners.model.Statement; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.UnicastProcessor; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java index c6907b21f..264043e90 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java @@ -18,14 +18,13 @@ import io.reactivesocket.Frame; import io.reactivesocket.FrameType; -import io.reactivex.functions.Predicate; -import io.reactivex.processors.UnicastProcessor; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.runner.Description; import org.junit.runners.model.Statement; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.UnicastProcessor; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @@ -42,12 +41,7 @@ public void testOnNext() throws Exception { rule.sendFrame(FrameType.NEXT); receiverSub.assertValueCount(1); - receiverSub.assertValue(new Predicate() { - @Override - public boolean test(Frame frame) throws Exception { - return frame.getType() == FrameType.NEXT; - } - }); + receiverSub.assertValue(frame -> frame.getType() == FrameType.NEXT); assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(false)); } @@ -57,12 +51,7 @@ public void testOnError() throws Exception { rule.sender.onError(new NullPointerException("deliberate test exception.")); receiverSub.assertValueCount(1); - receiverSub.assertValue(new Predicate() { - @Override - public boolean test(Frame frame) throws Exception { - return frame.getType() == FrameType.ERROR; - } - }); + receiverSub.assertValue(frame -> frame.getType() == FrameType.ERROR); receiverSub.assertError(NullPointerException.class); receiverSub.assertNotComplete(); assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(true)); @@ -74,12 +63,7 @@ public void testOnComplete() throws Exception { rule.sender.onComplete(); receiverSub.assertValueCount(1); - receiverSub.assertValue(new Predicate() { - @Override - public boolean test(Frame frame) throws Exception { - return frame.getType() == FrameType.COMPLETE || frame.getType() == FrameType.NEXT_COMPLETE; - } - }); + receiverSub.assertValue(frame -> frame.getType() == FrameType.COMPLETE || frame.getType() == FrameType.NEXT_COMPLETE); receiverSub.assertNoErrors(); receiverSub.assertComplete(); @@ -93,12 +77,7 @@ public void testTransportCancel() throws Exception { rule.sendFrame(FrameType.NEXT); receiverSub.assertValueCount(1); - receiverSub.assertValue(new Predicate() { - @Override - public boolean test(Frame frame) throws Exception { - return frame.getType() == FrameType.NEXT; - } - }); + receiverSub.assertValue(frame -> frame.getType() == FrameType.NEXT); receiverSub.cancel();// Transport cancel. assertThat("Sender not cleaned up.", rule.senderCleanedUp, is(true)); @@ -113,12 +92,7 @@ public void testRemoteCancel() throws Exception { rule.sendFrame(FrameType.NEXT); receiverSub.assertValueCount(1); - receiverSub.assertValue(new Predicate() { - @Override - public boolean test(Frame frame) throws Exception { - return frame.getType() == FrameType.NEXT; - } - }); + receiverSub.assertValue(frame -> frame.getType() == FrameType.NEXT); rule.sender.acceptCancelFrame(Frame.Cancel.from(rule.streamId));// Remote cancel. assertThat("Sender not cleaned up.", rule.senderCleanedUp, is(true)); @@ -135,12 +109,7 @@ public void testOnCompleteWithBuffer() throws Exception { receiverSub.request(1); // Now get completion receiverSub.assertValueCount(1); - receiverSub.assertValue(new Predicate() { - @Override - public boolean test(Frame frame) throws Exception { - return frame.getType() == FrameType.COMPLETE || frame.getType() == FrameType.NEXT_COMPLETE; - } - }); + receiverSub.assertValue(frame -> frame.getType() == FrameType.COMPLETE || frame.getType() == FrameType.NEXT_COMPLETE); receiverSub.assertNoErrors(); receiverSub.assertComplete(); assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(true)); @@ -155,12 +124,7 @@ public void testOnErrorWithBuffer() throws Exception { receiverSub.request(1); // Now get completion receiverSub.assertValueCount(1); - receiverSub.assertValue(new Predicate() { - @Override - public boolean test(Frame frame) throws Exception { - return frame.getType() == FrameType.ERROR; - } - }); + receiverSub.assertValue(frame -> frame.getType() == FrameType.ERROR); receiverSub.assertError(NullPointerException.class); receiverSub.assertNotComplete(); assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(true)); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseEnforcingSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseEnforcingSocketTest.java index 4f9739954..f9caa2edc 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseEnforcingSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseEnforcingSocketTest.java @@ -20,10 +20,10 @@ import io.reactivesocket.exceptions.RejectedException; import io.reactivesocket.lease.DefaultLeaseEnforcingSocketTest.SocketHolder; import io.reactivesocket.test.util.MockReactiveSocket; -import io.reactivex.processors.PublishProcessor; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import reactor.core.publisher.DirectProcessor; import java.util.Arrays; import java.util.Collection; @@ -65,10 +65,10 @@ public static class SocketHolder { private final MockReactiveSocket mockSocket; private final DefaultLeaseEnforcingSocket reactiveSocket; - private final PublishProcessor leaseTicks; + private final DirectProcessor leaseTicks; public SocketHolder(MockReactiveSocket mockSocket, DefaultLeaseEnforcingSocket reactiveSocket, - PublishProcessor leaseTicks) { + DirectProcessor leaseTicks) { this.mockSocket = mockSocket; this.reactiveSocket = reactiveSocket; this.reactiveSocket.acceptLeaseSender(lease -> {}); @@ -85,7 +85,7 @@ public DefaultLeaseHonoringSocket getReactiveSocket() { public static SocketHolder newHolder(LongSupplier currentTimeSupplier, int permits, int ttl) { LongSupplier _currentTimeSupplier = null == currentTimeSupplier? () -> -1 : currentTimeSupplier; - PublishProcessor leaseTicks = PublishProcessor.create(); + DirectProcessor leaseTicks = DirectProcessor.create(); FairLeaseDistributor distributor = new FairLeaseDistributor(() -> permits, ttl, leaseTicks); AbstractSocketRule rule = new AbstractSocketRule() { @Override diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseHonoringSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseHonoringSocketTest.java index d718d17c7..663e91aa0 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseHonoringSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseHonoringSocketTest.java @@ -17,20 +17,13 @@ package io.reactivesocket.lease; import io.reactivesocket.Frame; -import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.exceptions.RejectedException; import io.reactivesocket.lease.DefaultLeaseHonoringSocketTest.SocketHolder; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.test.util.MockReactiveSocket; -import io.reactivesocket.util.PayloadImpl; -import org.junit.Rule; -import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; -import io.reactivex.subscribers.TestSubscriber; import java.util.Arrays; import java.util.Collection; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseTest.java index c81ec785c..440ce3176 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseTest.java @@ -18,12 +18,12 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.test.util.MockReactiveSocket; import io.reactivesocket.util.PayloadImpl; import org.junit.Test; import org.junit.runners.Parameterized.Parameter; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.Flux; import java.util.function.LongSupplier; @@ -71,7 +71,7 @@ public void testRequestStream() throws Exception { public void testRequestChannel() throws Exception { T state = init(); TestSubscriber subscriber = TestSubscriber.create(); - getReactiveSocket(state).requestChannel(Px.just(PayloadImpl.EMPTY)).subscribe(subscriber); + getReactiveSocket(state).requestChannel(Flux.just(PayloadImpl.EMPTY)).subscribe(subscriber); subscriber.assertError(expectedException); getMockSocket(state).assertRequestChannelCount(expectedInvocations); } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisableLeaseSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisableLeaseSocketTest.java index adf11afbf..d231c8524 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisableLeaseSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisableLeaseSocketTest.java @@ -18,11 +18,11 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.util.PayloadImpl; import org.junit.Rule; import org.junit.Test; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.Flux; public class DisableLeaseSocketTest { @@ -56,7 +56,7 @@ public void testRequestStream() throws Exception { @Test(timeout = 10000) public void testRequestChannel() throws Exception { TestSubscriber subscriber = TestSubscriber.create(); - socketRule.getReactiveSocket().requestChannel(Px.just(PayloadImpl.EMPTY)).subscribe(subscriber); + socketRule.getReactiveSocket().requestChannel(Flux.just(PayloadImpl.EMPTY)).subscribe(subscriber); subscriber.assertError(UnsupportedOperationException.class); socketRule.getMockSocket().assertRequestChannelCount(1); } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocketTest.java index eaa099b3e..22cfeca89 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DisabledLeaseAcceptingSocketTest.java @@ -18,11 +18,11 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.util.PayloadImpl; import org.junit.Rule; import org.junit.Test; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.Flux; public class DisabledLeaseAcceptingSocketTest { @Rule @@ -55,7 +55,7 @@ public void testRequestStream() throws Exception { @Test(timeout = 10000) public void testRequestChannel() throws Exception { TestSubscriber subscriber = TestSubscriber.create(); - socketRule.getReactiveSocket().requestChannel(Px.just(PayloadImpl.EMPTY)).subscribe(subscriber); + socketRule.getReactiveSocket().requestChannel(Flux.just(PayloadImpl.EMPTY)).subscribe(subscriber); subscriber.assertError(UnsupportedOperationException.class); socketRule.getMockSocket().assertRequestChannelCount(1); } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/FairLeaseDistributorTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/FairLeaseDistributorTest.java index 3f610e129..7cd767a50 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/FairLeaseDistributorTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/FairLeaseDistributorTest.java @@ -16,13 +16,13 @@ package io.reactivesocket.lease; -import io.reactivesocket.reactivestreams.extensions.internal.Cancellable; -import io.reactivex.processors.PublishProcessor; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import reactor.core.Disposable; +import reactor.core.publisher.DirectProcessor; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; @@ -37,13 +37,13 @@ public class FairLeaseDistributorTest { @Test public void testRegisterCancel() throws Exception { - Cancellable cancel = rule.distributor.registerSocket(rule); + Disposable disposable = rule.distributor.registerSocket(rule); rule.ticks.onNext(1L); assertThat("Unexpected leases received.", rule.leases, hasSize(1)); Lease lease = rule.leases.remove(0); assertThat("Unexpected permits", lease.getAllowedRequests(), is(rule.permits)); rule.assertTTL(lease); - cancel.cancel(); + disposable.dispose(); rule.ticks.onNext(1L); assertThat("Unexpected leases received post cancellation.", rule.leases, is(empty())); } @@ -62,13 +62,13 @@ public void testTwoSockets() throws Exception { @Test public void testTwoSocketsAndCancel() throws Exception { rule.permits = 2; - Cancellable cancel1 = rule.distributor.registerSocket(rule); + Disposable disposable = rule.distributor.registerSocket(rule); rule.distributor.registerSocket(rule); rule.ticks.onNext(1L); assertThat("Unexpected leases received.", rule.leases, hasSize(2)); rule.assertLease(rule.permits/2); rule.assertLease(rule.permits/2); - cancel1.cancel(); + disposable.dispose(); rule.ticks.onNext(1L); assertThat("Unexpected leases received.", rule.leases, hasSize(1)); } @@ -78,13 +78,13 @@ public void testRedistribute() throws Exception { rule.permits = 2; rule.redistributeLeasesOnConnect(); - Cancellable cancel1 = rule.distributor.registerSocket(rule); + Disposable disposable = rule.distributor.registerSocket(rule); rule.distributor.registerSocket(rule); assertThat("Unexpected leases received.", rule.leases, hasSize(2)); rule.assertLease(rule.permits/2); rule.assertLease(rule.permits/2); - cancel1.cancel(); + disposable.dispose(); rule.ticks.onNext(1L); assertThat("Unexpected leases received.", rule.leases, hasSize(1)); } @@ -95,7 +95,7 @@ public static class DistributorRule extends ExternalResource implements Consumer private FairLeaseDistributor distributor; private int permits; private int ttl; - private PublishProcessor ticks; + private DirectProcessor ticks; private CopyOnWriteArrayList leases; @Override @@ -110,7 +110,7 @@ public void evaluate() throws Throwable { } protected void init() { - ticks = PublishProcessor.create(); + ticks = DirectProcessor.create(); if (0 == permits) { permits = 1; } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/LocalDuplexConnection.java b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/LocalDuplexConnection.java index 50fb05c1a..82a10c3c9 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/LocalDuplexConnection.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/LocalDuplexConnection.java @@ -18,37 +18,36 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.EmptySubject; -import io.reactivex.Flowable; -import io.reactivex.processors.PublishProcessor; import org.reactivestreams.Publisher; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; public class LocalDuplexConnection implements DuplexConnection { - private final PublishProcessor send; - private final PublishProcessor receive; - private final EmptySubject closeNotifier; + private final DirectProcessor send; + private final DirectProcessor receive; + private final MonoProcessor closeNotifier; private final String name; - public LocalDuplexConnection(String name, PublishProcessor send, PublishProcessor receive) { + public LocalDuplexConnection(String name, DirectProcessor send, DirectProcessor receive) { this.name = name; this.send = send; this.receive = receive; - closeNotifier = new EmptySubject(); + closeNotifier = MonoProcessor.create(); } @Override - public Publisher send(Publisher frame) { - return Flowable - .fromPublisher(frame) + public Mono send(Publisher frame) { + return Mono + .from(frame) .doOnNext(send::onNext) .doOnError(send::onError) - .ignoreElements() - .toFlowable(); + .then(); } @Override - public Publisher receive() { + public Flux receive() { return receive; } @@ -58,15 +57,15 @@ public double availability() { } @Override - public Publisher close() { - return Px.defer(() -> { + public Mono close() { + return Mono.defer(() -> { closeNotifier.onComplete(); - return Px.empty(); + return Mono.empty(); }); } @Override - public Publisher onClose() { + public Mono onClose() { return closeNotifier; } } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/MockReactiveSocket.java b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/MockReactiveSocket.java index 4540388d1..425c34edb 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/MockReactiveSocket.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/MockReactiveSocket.java @@ -18,8 +18,9 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.reactivestreams.extensions.Px; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.concurrent.atomic.AtomicInteger; @@ -47,32 +48,32 @@ public MockReactiveSocket(ReactiveSocket delegate) { } @Override - public final Publisher fireAndForget(Payload payload) { - return Px.from(delegate.fireAndForget(payload)) + public final Mono fireAndForget(Payload payload) { + return delegate.fireAndForget(payload) .doOnSubscribe(s -> fnfCount.incrementAndGet()); } @Override - public final Publisher requestResponse(Payload payload) { - return Px.from(delegate.requestResponse(payload)) + public final Mono requestResponse(Payload payload) { + return delegate.requestResponse(payload) .doOnSubscribe(s -> rrCount.incrementAndGet()); } @Override - public final Publisher requestStream(Payload payload) { - return Px.from(delegate.requestStream(payload)) + public final Flux requestStream(Payload payload) { + return delegate.requestStream(payload) .doOnSubscribe(s -> rStreamCount.incrementAndGet()); } @Override - public final Publisher requestChannel(Publisher payloads) { - return Px.from(delegate.requestChannel(payloads)) + public final Flux requestChannel(Publisher payloads) { + return delegate.requestChannel(payloads) .doOnSubscribe(s -> rChannelCount.incrementAndGet()); } @Override - public final Publisher metadataPush(Payload payload) { - return Px.from(delegate.metadataPush(payload)) + public final Mono metadataPush(Payload payload) { + return delegate.metadataPush(payload) .doOnSubscribe(s -> pushCount.incrementAndGet()); } @@ -82,12 +83,12 @@ public double availability() { } @Override - public Publisher close() { + public Mono close() { return delegate.close(); } @Override - public Publisher onClose() { + public Mono onClose() { return delegate.onClose(); } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/TestDuplexConnection.java b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/TestDuplexConnection.java index d653702ce..fc09d8aa8 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/TestDuplexConnection.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/TestDuplexConnection.java @@ -18,15 +18,14 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; -import io.reactivex.Flowable; -import io.reactivex.processors.PublishProcessor; -import io.reactivex.processors.UnicastProcessor; -import org.reactivestreams.Processor; import org.reactivestreams.Publisher; -import io.reactivesocket.reactivestreams.extensions.Px; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.reactivex.subscribers.TestSubscriber; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; import java.util.Collection; import java.util.concurrent.ConcurrentLinkedQueue; @@ -40,28 +39,28 @@ public class TestDuplexConnection implements DuplexConnection { private static final Logger logger = LoggerFactory.getLogger(TestDuplexConnection.class); private final LinkedBlockingQueue sent; - private final UnicastProcessor sentPublisher; - private final UnicastProcessor received; - private final Processor close; + private final DirectProcessor sentPublisher; + private final DirectProcessor received; + private final MonoProcessor close; private final ConcurrentLinkedQueue> sendSubscribers; private volatile double availability =1; private volatile int initialSendRequestN = Integer.MAX_VALUE; public TestDuplexConnection() { sent = new LinkedBlockingQueue<>(); - received = UnicastProcessor.create(); - sentPublisher = UnicastProcessor.create(); + received = DirectProcessor.create(); + sentPublisher = DirectProcessor.create(); sendSubscribers = new ConcurrentLinkedQueue<>(); - close = PublishProcessor.create(); + close = MonoProcessor.create(); } @Override - public Publisher send(Publisher frames) { + public Mono send(Publisher frames) { if (availability <= 0) { - return Px.error(new IllegalStateException("ReactiveSocket not available. Availability: " + availability)); + return Mono.error(new IllegalStateException("ReactiveSocket not available. Availability: " + availability)); } TestSubscriber subscriber = TestSubscriber.create(initialSendRequestN); - Flowable.fromPublisher(frames) + Flux.from(frames) .doOnNext(frame -> { sent.offer(frame); sentPublisher.onNext(frame); @@ -71,11 +70,11 @@ public Publisher send(Publisher frames) { }) .subscribe(subscriber); sendSubscribers.add(subscriber); - return Px.empty(); + return Mono.empty(); } @Override - public Publisher receive() { + public Flux receive() { return received; } @@ -85,12 +84,12 @@ public double availability() { } @Override - public Publisher close() { + public Mono close() { return close; } @Override - public Publisher onClose() { + public Mono onClose() { return close(); } diff --git a/reactivesocket-discovery-eureka/src/main/java/io/reactivesocket/discovery/eureka/Eureka.java b/reactivesocket-discovery-eureka/src/main/java/io/reactivesocket/discovery/eureka/Eureka.java index c3def30ff..444e3c217 100644 --- a/reactivesocket-discovery-eureka/src/main/java/io/reactivesocket/discovery/eureka/Eureka.java +++ b/reactivesocket-discovery-eureka/src/main/java/io/reactivesocket/discovery/eureka/Eureka.java @@ -20,7 +20,7 @@ import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.discovery.CacheRefreshedEvent; import com.netflix.discovery.EurekaClient; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; +import io.reactivesocket.internal.ValidatingSubscription; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; diff --git a/reactivesocket-discovery-eureka/src/test/java/io/reactivesocket/discovery/eureka/EurekaTest.java b/reactivesocket-discovery-eureka/src/test/java/io/reactivesocket/discovery/eureka/EurekaTest.java index 3f4635b41..836aa0e84 100644 --- a/reactivesocket-discovery-eureka/src/test/java/io/reactivesocket/discovery/eureka/EurekaTest.java +++ b/reactivesocket-discovery-eureka/src/test/java/io/reactivesocket/discovery/eureka/EurekaTest.java @@ -22,7 +22,6 @@ import com.netflix.discovery.CacheRefreshedEvent; import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaEventListener; -import io.reactivex.Flowable; import io.reactivex.subscribers.TestSubscriber; import org.hamcrest.MatcherAssert; import org.junit.Test; @@ -31,6 +30,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; +import org.reactivestreams.Publisher; import java.net.SocketAddress; import java.util.ArrayList; @@ -55,7 +55,7 @@ public void testFilterNonUp() throws Exception { final ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(EurekaEventListener.class); - Flowable> src = Flowable.fromPublisher(eureka.subscribeToAsg("vip-1", false)); + Publisher> src = eureka.subscribeToAsg("vip-1", false); TestSubscriber> testSubscriber = new TestSubscriber<>(); src.subscribe(testSubscriber); diff --git a/reactivesocket-examples/build.gradle b/reactivesocket-examples/build.gradle index ea3fec1ca..a584ac54c 100644 --- a/reactivesocket-examples/build.gradle +++ b/reactivesocket-examples/build.gradle @@ -37,7 +37,7 @@ dependencies { compile project(':reactivesocket-client') compile project(':reactivesocket-discovery-eureka') compile project(':reactivesocket-spectator') - compile project(':reactivesocket-transport-tcp') + compile project(':reactivesocket-transport-netty') compile project(':reactivesocket-transport-local') compile project(':reactivesocket-test') diff --git a/reactivesocket-examples/src/jmh/java/io/reactivesocket/perf/AbstractReactiveSocketPerf.java b/reactivesocket-examples/src/jmh/java/io/reactivesocket/perf/AbstractReactiveSocketPerf.java index 15ba3004f..42be664c0 100644 --- a/reactivesocket-examples/src/jmh/java/io/reactivesocket/perf/AbstractReactiveSocketPerf.java +++ b/reactivesocket-examples/src/jmh/java/io/reactivesocket/perf/AbstractReactiveSocketPerf.java @@ -20,12 +20,15 @@ import io.reactivesocket.perf.util.AbstractMicrobenchmarkBase; import io.reactivesocket.perf.util.BlackholeSubscriber; import io.reactivesocket.perf.util.ClientServerHolder; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.transport.netty.server.TcpTransportServer; import io.reactivex.Flowable; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.infra.Blackhole; +import reactor.ipc.netty.tcp.TcpClient; +import reactor.ipc.netty.tcp.TcpServer; +import java.net.InetSocketAddress; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; @@ -45,8 +48,9 @@ public class AbstractReactiveSocketPerf extends AbstractMicrobenchmarkBase { protected Supplier multiClientTcpHolders; protected void _setup(Blackhole bh) { - tcpHolder = ClientServerHolder.create(TcpTransportServer.create(), - socketAddress -> TcpTransportClient.create(socketAddress)); + tcpHolder = ClientServerHolder.create(TcpTransportServer.create(TcpServer.create()), socketAddress -> + TcpTransportClient.create(TcpClient.create(options -> options.connect((InetSocketAddress)socketAddress))) + ); String clientName = "local-" + ThreadLocalRandom.current().nextInt(); localHolder = ClientServerHolder.create(LocalServer.create(clientName), socketAddress -> LocalClient.create(clientName)); diff --git a/reactivesocket-examples/src/jmh/java/io/reactivesocket/perf/util/ClientServerHolder.java b/reactivesocket-examples/src/jmh/java/io/reactivesocket/perf/util/ClientServerHolder.java index 02ccca8bd..f516a1245 100644 --- a/reactivesocket-examples/src/jmh/java/io/reactivesocket/perf/util/ClientServerHolder.java +++ b/reactivesocket-examples/src/jmh/java/io/reactivesocket/perf/util/ClientServerHolder.java @@ -20,17 +20,20 @@ import io.reactivesocket.client.ReactiveSocketClient; import io.reactivesocket.client.SetupProvider; import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.transport.TransportClient; import io.reactivesocket.transport.TransportServer; import io.reactivesocket.transport.TransportServer.StartedServer; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.transport.netty.server.TcpTransportServer; import io.reactivesocket.util.PayloadImpl; import io.reactivex.Flowable; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.tcp.TcpClient; +import reactor.ipc.netty.tcp.TcpServer; +import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicInteger; @@ -61,10 +64,12 @@ public static ClientServerHolder create(TransportServer transportServer, } public static Supplier requestResponseMultiTcp(int clientCount) { - StartedServer server = startServer(TcpTransportServer.create(), new Handler()); + StartedServer server = startServer(TcpTransportServer.create(TcpServer.create()), new Handler()); final ReactiveSocket[] sockets = new ReactiveSocket[clientCount]; for (int i = 0; i < clientCount; i++) { - sockets[i] = newClient(server.getServerAddress(), sock -> TcpTransportClient.create(sock)); + sockets[i] = newClient(server.getServerAddress(), sock -> + TcpTransportClient.create(TcpClient.create(options -> options.connect((InetSocketAddress)sock))) + ); } return new Supplier() { @@ -96,13 +101,13 @@ private static ReactiveSocket newClient(SocketAddress serverAddress, private static class Handler extends AbstractReactiveSocket { @Override - public Publisher requestResponse(Payload payload) { - return Px.just(new PayloadImpl(HELLO)); + public Mono requestResponse(Payload payload) { + return Mono.just(new PayloadImpl(HELLO)); } @Override - public Publisher requestStream(Payload payload) { - return Flowable.range(1, Integer.MAX_VALUE) + public Flux requestStream(Payload payload) { + return Flux.range(1, Integer.MAX_VALUE) .map(integer -> new PayloadImpl(HELLO)); } } diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/channel/ChannelEchoClient.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/channel/ChannelEchoClient.java index a8e55a3a6..9ebbfa3bc 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/channel/ChannelEchoClient.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/channel/ChannelEchoClient.java @@ -24,15 +24,17 @@ import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.server.ReactiveSocketServer.SocketAcceptor; import io.reactivesocket.transport.TransportServer.StartedServer; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.transport.netty.server.TcpTransportServer; import io.reactivesocket.util.PayloadImpl; -import io.reactivesocket.util.ReactiveSocketDecorator; -import io.reactivex.Flowable; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.ipc.netty.tcp.TcpClient; +import reactor.ipc.netty.tcp.TcpServer; +import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; +import java.time.Duration; import static io.reactivesocket.client.KeepAliveProvider.*; import static io.reactivesocket.client.SetupProvider.*; @@ -40,25 +42,25 @@ public final class ChannelEchoClient { public static void main(String[] args) { - StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) + StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) .start(new SocketAcceptorImpl()); SocketAddress address = server.getServerAddress(); - ReactiveSocket socket = Flowable.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(address), - keepAlive(never()).disableLease()) - .connect()) - .blockingFirst(); + ReactiveSocket socket = ReactiveSocketClient.create( + TcpTransportClient.create(TcpClient.create(options -> options.connect((InetSocketAddress)address))), + keepAlive(never()).disableLease() + ).connect().block(); - Flowable.fromPublisher(socket.requestChannel(Flowable.interval(0, 100, TimeUnit.MILLISECONDS) + socket.requestChannel(Flux.interval(Duration.ofMillis(100)) .map(i -> "Hello - " + i) .map(PayloadImpl::new) - .repeat())) + .repeat()) .map(payload -> payload.getData()) .map(ByteBufferUtil::toUtf8String) .doOnNext(System.out::println) .take(10) - .concatWith(Flowable.fromPublisher(socket.close()).cast(String.class)) - .blockingLast(); + .concatWith(socket.close().cast(String.class)) + .blockLast(); } private static class SocketAcceptorImpl implements SocketAcceptor { @@ -66,8 +68,8 @@ private static class SocketAcceptorImpl implements SocketAcceptor { public LeaseEnforcingSocket accept(ConnectionSetupPayload setupPayload, ReactiveSocket reactiveSocket) { return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { @Override - public Publisher requestChannel(Publisher payloads) { - return Flowable.fromPublisher(payloads) + public Flux requestChannel(Publisher payloads) { + return Flux.from(payloads) .map(Payload::getData) .map(ByteBufferUtil::toUtf8String) .map(s -> "Echo: " + s) diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/duplex/DuplexClient.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/duplex/DuplexClient.java index 7bcdf7a48..ade9be226 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/duplex/DuplexClient.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/duplex/DuplexClient.java @@ -23,14 +23,16 @@ import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.transport.TransportServer.StartedServer; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.transport.netty.server.TcpTransportServer; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.ipc.netty.tcp.TcpClient; +import reactor.ipc.netty.tcp.TcpServer; +import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; +import java.time.Duration; import static io.reactivesocket.client.KeepAliveProvider.*; import static io.reactivesocket.client.SetupProvider.*; @@ -38,32 +40,33 @@ public final class DuplexClient { public static void main(String[] args) { - StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) + StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) .start((setupPayload, reactiveSocket) -> { - Flowable.fromPublisher(reactiveSocket.requestStream(new PayloadImpl("Hello-Bidi"))) + reactiveSocket.requestStream(new PayloadImpl("Hello-Bidi")) .map(Payload::getData) .map(ByteBufferUtil::toUtf8String) - .forEach(System.out::println); + .log() + .subscribe(); return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { }); }); SocketAddress address = server.getServerAddress(); - ReactiveSocketClient rsclient = ReactiveSocketClient.createDuplex(TcpTransportClient.create(address), - new SocketAcceptor() { + ReactiveSocketClient rsclient = ReactiveSocketClient.createDuplex(TcpTransportClient.create(TcpClient.create(options -> + options.connect((InetSocketAddress)address))), new SocketAcceptor() { @Override public LeaseEnforcingSocket accept(ReactiveSocket reactiveSocket) { return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { @Override - public Publisher requestStream(Payload payload) { - return Flowable.interval(0, 1, TimeUnit.SECONDS).map(aLong -> new PayloadImpl("Bi-di Response => " + aLong)); + public Flux requestStream(Payload payload) { + return Flux.interval(Duration.ofSeconds(1)).map(aLong -> new PayloadImpl("Bi-di Response => " + aLong)); } }); } }, keepAlive(never()).disableLease()); - ReactiveSocket socket = Flowable.fromPublisher(rsclient.connect()).blockingFirst(); + ReactiveSocket socket = rsclient.connect().block(); - Flowable.fromPublisher(socket.onClose()).ignoreElements().blockingAwait(); + socket.onClose().block(); } } diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/requestresponse/HelloWorldClient.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/requestresponse/HelloWorldClient.java index 823241702..72584dd4e 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/requestresponse/HelloWorldClient.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/requestresponse/HelloWorldClient.java @@ -24,12 +24,14 @@ import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.transport.TransportServer.StartedServer; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.transport.netty.server.TcpTransportServer; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.tcp.TcpClient; +import reactor.ipc.netty.tcp.TcpServer; +import java.net.InetSocketAddress; import java.net.SocketAddress; import static io.reactivesocket.client.KeepAliveProvider.*; @@ -39,27 +41,28 @@ public final class HelloWorldClient { public static void main(String[] args) { - ReactiveSocketServer s = ReactiveSocketServer.create(TcpTransportServer.create()); + ReactiveSocketServer s = ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())); StartedServer server = s.start((setupPayload, reactiveSocket) -> { return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { @Override - public Publisher requestResponse(Payload p) { - return Flowable.just(p); + public Mono requestResponse(Payload p) { + return Mono.just(p); } }); }); SocketAddress address = server.getServerAddress(); - ReactiveSocketClient client = ReactiveSocketClient.create(TcpTransportClient.create(address), + ReactiveSocketClient client = ReactiveSocketClient.create(TcpTransportClient.create(TcpClient.create(options -> + options.connect((InetSocketAddress)address))), keepAlive(never()).disableLease()); - ReactiveSocket socket = Flowable.fromPublisher(client.connect()).singleOrError().blockingGet(); + ReactiveSocket socket = client.connect().block(); - Flowable.fromPublisher(socket.requestResponse(new PayloadImpl("Hello"))) + socket.requestResponse(new PayloadImpl("Hello")) .map(payload -> payload.getData()) .map(ByteBufferUtil::toUtf8String) .doOnNext(System.out::println) - .concatWith(Flowable.fromPublisher(socket.close()).cast(String.class)) + .concatWith(socket.close().cast(String.class)) .ignoreElements() - .blockingAwait(); + .block(); } } diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stream/StreamingClient.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stream/StreamingClient.java index 6110a144f..95095e03e 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stream/StreamingClient.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stream/StreamingClient.java @@ -24,14 +24,16 @@ import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.server.ReactiveSocketServer.SocketAcceptor; import io.reactivesocket.transport.TransportServer.StartedServer; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.transport.netty.server.TcpTransportServer; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.ipc.netty.tcp.TcpClient; +import reactor.ipc.netty.tcp.TcpServer; +import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; +import java.time.Duration; import static io.reactivesocket.client.KeepAliveProvider.*; import static io.reactivesocket.client.SetupProvider.*; @@ -39,22 +41,23 @@ public final class StreamingClient { public static void main(String[] args) { - StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) + StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) .start(new SocketAcceptorImpl()); SocketAddress address = server.getServerAddress(); - ReactiveSocket socket = Flowable.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(address), + ReactiveSocket socket = ReactiveSocketClient.create(TcpTransportClient.create(TcpClient.create(options -> + options.connect((InetSocketAddress)address))), keepAlive(never()).disableLease()) - .connect()) - .blockingFirst(); + .connect() + .block(); - Flowable.fromPublisher(socket.requestStream(new PayloadImpl("Hello"))) + socket.requestStream(new PayloadImpl("Hello")) .map(payload -> payload.getData()) .map(ByteBufferUtil::toUtf8String) .doOnNext(System.out::println) .take(10) - .concatWith(Flowable.fromPublisher(socket.close()).cast(String.class)) - .blockingLast(); + .thenEmpty(socket.close()) + .block(); } private static class SocketAcceptorImpl implements SocketAcceptor { @@ -62,8 +65,8 @@ private static class SocketAcceptorImpl implements SocketAcceptor { public LeaseEnforcingSocket accept(ConnectionSetupPayload setupPayload, ReactiveSocket reactiveSocket) { return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { @Override - public Publisher requestStream(Payload payload) { - return Flowable.interval(100, TimeUnit.MILLISECONDS) + public Flux requestStream(Payload payload) { + return Flux.interval(Duration.ofMillis(100)) .map(aLong -> new PayloadImpl("Interval: " + aLong)); } }); diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/StressTest.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/StressTest.java index ac87c0e73..dee218982 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/StressTest.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/StressTest.java @@ -17,12 +17,12 @@ import io.reactivesocket.client.LoadBalancingClient; import io.reactivesocket.exceptions.RejectedException; import io.reactivesocket.server.ReactiveSocketServer; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; -import io.reactivex.Flowable; -import io.reactivex.Single; -import io.reactivex.disposables.Disposable; +import io.reactivesocket.transport.netty.server.TcpTransportServer; import org.HdrHistogram.Recorder; import org.reactivestreams.Publisher; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.ipc.netty.tcp.TcpServer; import java.net.SocketAddress; import java.time.Duration; @@ -59,10 +59,10 @@ class StressTest { } public StressTest printStatsEvery(Duration duration) { - printDisposable = Flowable.interval(duration.getSeconds(), TimeUnit.SECONDS) - .forEach(aLong -> { + printDisposable = Flux.interval(duration) + .doOnEach(aLong -> { printTestStats(false); - }); + }).subscribe(); return this; } @@ -90,7 +90,7 @@ public void printTestStats(boolean printLatencyDistribution) { public StressTest startClient() { LoadBalancingClient client = LoadBalancingClient.create(getServerList(), address -> config.newClientForServer(address)); - clientSocket = Single.fromPublisher(client.connect()).blockingGet(); + clientSocket = client.connect().block(); System.out.println("Client ready!"); return this; } @@ -98,7 +98,7 @@ public StressTest startClient() { private Publisher> getServerList() { return config.serverListChangeTicks() .map(aLong -> startServer()) - .map(new io.reactivex.functions.Function>() { + .map(new Function>() { private final List addresses = new ArrayList(); @Override @@ -123,7 +123,7 @@ public void startTest(Function> testFunction) { while (System.nanoTime() - testStartTime < config.getTestDuration().toNanos()) { if (outstandings.get() <= config.getMaxConcurrency()) { AtomicLong startTime = new AtomicLong(); - Flowable.defer(() -> testFunction.apply(clientSocket)) + Flux.defer(() -> testFunction.apply(clientSocket)) .doOnSubscribe(subscription -> { startTime.set(System.nanoTime()); outstandings.incrementAndGet(); @@ -136,7 +136,7 @@ public void startTest(Function> testFunction) { .doOnComplete(() -> { successes.incrementAndGet(); }) - .onErrorResumeNext(e -> { + .onErrorResumeWith(e -> { failures.incrementAndGet(); if (e instanceof RejectedException) { leaseExhausted.incrementAndGet(); @@ -146,7 +146,7 @@ public void startTest(Function> testFunction) { if (failures.get() % 1000 == 0) { e.printStackTrace(); } - return Flowable.empty(); + return Flux.empty(); }) .subscribe(); } else { @@ -161,7 +161,7 @@ public void startTest(Function> testFunction) { System.out.println("Stress test finished. Duration (minutes): " + Duration.ofNanos(System.nanoTime() - testStartTime).toMinutes()); printTestStats(true); - Flowable.fromPublisher(clientSocket.close()).ignoreElements().blockingGet(); + clientSocket.close().block(); if (null != printDisposable) { printDisposable.dispose(); @@ -169,7 +169,7 @@ public void startTest(Function> testFunction) { } private SocketAddress startServer() { - return ReactiveSocketServer.create(TcpTransportServer.create()) + return ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) .start((setup, sendingSocket) -> { return config.nextServerHandler(serverCount.incrementAndGet()); }) diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/StressTestHandler.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/StressTestHandler.java index 0fec1e48a..6b557603c 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/StressTestHandler.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/StressTestHandler.java @@ -17,31 +17,30 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; -import java.util.concurrent.Callable; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Supplier; class StressTestHandler extends AbstractReactiveSocket { - private final Callable failureSelector; + private final Supplier failureSelector; - private StressTestHandler(Callable failureSelector) { + private StressTestHandler(Supplier failureSelector) { this.failureSelector = failureSelector; } @Override - public Publisher requestResponse(Payload payload) { - return Flowable.defer(() -> { - Result result = failureSelector.call(); + public Mono requestResponse(Payload payload) { + return Mono.defer(() -> { + Result result = failureSelector.get(); switch (result) { case Fail: - return Flowable.error(new Exception("SERVER EXCEPTION")); + return Mono.error(new Exception("SERVER EXCEPTION")); case DontReply: - return Flowable.never(); // Cause timeout + return Mono.never(); // Cause timeout default: - return Flowable.just(new PayloadImpl("Response")); + return Mono.just(new PayloadImpl("Response")); } }); } diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/TestConfig.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/TestConfig.java index a5b605158..623e1020e 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/TestConfig.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stress/TestConfig.java @@ -21,10 +21,11 @@ import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.lease.FairLeaseDistributor; import io.reactivesocket.lease.LeaseEnforcingSocket; -import io.reactivesocket.reactivestreams.extensions.ExecutorServiceBasedScheduler; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivex.Flowable; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import reactor.core.publisher.Flux; +import reactor.ipc.netty.tcp.TcpClient; +import java.net.InetSocketAddress; import java.net.SocketAddress; import java.time.Duration; import java.util.function.IntSupplier; @@ -32,7 +33,6 @@ import static io.reactivesocket.client.filter.ReactiveSocketClients.*; import static io.reactivesocket.client.filter.ReactiveSockets.*; import static io.reactivesocket.examples.transport.tcp.stress.StressTestHandler.*; -import static java.util.concurrent.TimeUnit.*; public class TestConfig { @@ -41,7 +41,6 @@ public class TestConfig { private final IntSupplier serverCapacitySupplier; private final int leaseTtlMillis; private final SetupProvider setupProvider; - private static final ExecutorServiceBasedScheduler scheduler = new ExecutorServiceBasedScheduler(); public TestConfig() { this(Duration.ofMinutes(1), 100, true); @@ -65,7 +64,7 @@ public TestConfig(Duration testDuration, int maxConcurrency, IntSupplier serverC this.maxConcurrency = maxConcurrency; this.serverCapacitySupplier = serverCapacitySupplier; this.leaseTtlMillis = leaseTtlMillis; - KeepAliveProvider keepAliveProvider = KeepAliveProvider.from(30_000, Flowable.interval(30, SECONDS)); + KeepAliveProvider keepAliveProvider = KeepAliveProvider.from(30_000, Flux.interval(Duration.ofSeconds(30))); SetupProvider setup = SetupProvider.keepAlive(keepAliveProvider); setupProvider = serverCapacitySupplier == null? setup.disableLease() : setup; } @@ -78,15 +77,16 @@ public final int getMaxConcurrency() { return maxConcurrency; } - public Flowable serverListChangeTicks() { - return Flowable.interval(2, SECONDS); + public Flux serverListChangeTicks() { + return Flux.interval(Duration.ofSeconds(2)); } public final ReactiveSocketClient newClientForServer(SocketAddress server) { - TcpTransportClient transport = TcpTransportClient.create(server); + TcpTransportClient transport = TcpTransportClient.create(TcpClient.create(options -> + options.connect((InetSocketAddress)server))); ReactiveSocketClient raw = ReactiveSocketClient.create(transport, setupProvider); - return wrap(detectFailures(connectTimeout(raw, 1, SECONDS, scheduler)), - timeout(1, SECONDS, scheduler)); + return wrap(detectFailures(connectTimeout(raw, Duration.ofSeconds(1))), + timeout(Duration.ofSeconds(1))); } public final LeaseEnforcingSocket nextServerHandler(int serverCount) { @@ -96,8 +96,7 @@ public final LeaseEnforcingSocket nextServerHandler(int serverCount) { return new DisabledLeaseAcceptingSocket(socket); } else { FairLeaseDistributor leaseDistributor = new FairLeaseDistributor(serverCapacitySupplier, leaseTtlMillis, - Flowable.interval(0, leaseTtlMillis, - MILLISECONDS)); + Flux.interval(Duration.ofMillis(leaseTtlMillis))); return new DefaultLeaseEnforcingSocket(socket, leaseDistributor); } } diff --git a/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest.java b/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest.java index 082aed564..b600b6876 100644 --- a/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest.java +++ b/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest.java @@ -25,18 +25,20 @@ import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.transport.TransportServer.StartedServer; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.transport.netty.server.TcpTransportServer; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import io.reactivex.Single; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.runner.Description; import org.junit.runners.model.Statement; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.tcp.TcpClient; +import reactor.ipc.netty.tcp.TcpServer; +import java.net.InetSocketAddress; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; @@ -51,16 +53,15 @@ public class IntegrationTest { @Test(timeout = 2_000L) public void testRequest() { - Flowable.fromPublisher(rule.client.requestResponse(new PayloadImpl("REQUEST", "META"))) - .blockingFirst(); + rule.client.requestResponse(new PayloadImpl("REQUEST", "META")).block(); assertThat("Server did not see the request.", rule.requestCount.get(), is(1)); } - @Test(timeout = 2_000L) + @Test//(timeout = 2_000L) public void testClose() throws ExecutionException, InterruptedException, TimeoutException { - Flowable.fromPublisher(rule.client.close()).ignoreElements().blockingGet(); - Thread.sleep(100); - assertThat("Server did not disconnect.", rule.disconnectionCounter.get(), is(1)); + + rule.client.close().block(); + rule.disconnectionCounter.await(); } public static class ClientServerRule extends ExternalResource { @@ -68,7 +69,7 @@ public static class ClientServerRule extends ExternalResource { private StartedServer server; private ReactiveSocket client; private AtomicInteger requestCount; - private AtomicInteger disconnectionCounter; + private CountDownLatch disconnectionCounter; @Override public Statement apply(final Statement base, Description description) { @@ -76,26 +77,27 @@ public Statement apply(final Statement base, Description description) { @Override public void evaluate() throws Throwable { requestCount = new AtomicInteger(); - disconnectionCounter = new AtomicInteger(); - server = ReactiveSocketServer.create(TcpTransportServer.create()) + disconnectionCounter = new CountDownLatch(1); + server = ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) .start((setup, sendingSocket) -> { - Flowable.fromPublisher(sendingSocket.onClose()) - .doOnTerminate(() -> disconnectionCounter.incrementAndGet()) + sendingSocket.onClose() + .doFinally(signalType -> disconnectionCounter.countDown()) .subscribe(); return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { @Override - public Publisher requestResponse(Payload payload) { - return Flowable.just(new PayloadImpl("RESPONSE", "METADATA")) + public Mono requestResponse(Payload payload) { + return Mono.just(new PayloadImpl("RESPONSE", "METADATA")) .doOnSubscribe(s -> requestCount.incrementAndGet()); } }); }); - client = Single.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(server.getServerAddress()), + client = ReactiveSocketClient.create(TcpTransportClient.create(TcpClient.create(options -> + options.connect((InetSocketAddress)server.getServerAddress()))), SetupProvider.keepAlive(KeepAliveProvider.never()) .disableLease()) - .connect()) - .blockingGet(); + .connect() + .block(); base.evaluate(); } }; diff --git a/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest2.java b/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest2.java deleted file mode 100644 index a52324e6d..000000000 --- a/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest2.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.reactivesocket.integration; - -import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.client.KeepAliveProvider; -import io.reactivesocket.client.ReactiveSocketClient; -import io.reactivesocket.client.SetupProvider; -import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; -import io.reactivesocket.server.ReactiveSocketServer; -import io.reactivesocket.transport.TransportServer; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; -import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import io.reactivex.Single; -import org.junit.Assert; -import org.junit.Test; - -import java.util.NoSuchElementException; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - -public class IntegrationTest2 { - // This will throw as it completes without any onNext - @Test(timeout = 2_000L, expected = NoSuchElementException.class) - public void testCompleteWithoutNext() throws InterruptedException { - ReactiveSocket requestHandler = mock(ReactiveSocket.class); - - when(requestHandler.requestStream(any())) - .thenReturn(Flowable.empty()); - - TransportServer.StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) - .start((setup, sendingSocket) -> { - Flowable.fromPublisher(sendingSocket.onClose()) - .subscribe(); - - return new DisabledLeaseAcceptingSocket(requestHandler); - }); - ReactiveSocket client = Single.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(server.getServerAddress()), - SetupProvider.keepAlive(KeepAliveProvider.never()) - .disableLease()) - .connect()) - .blockingGet(); - - Flowable.fromPublisher(client.requestStream(new PayloadImpl("REQUEST", "META"))) - .blockingFirst(); - - Thread.sleep(100); - verify(requestHandler).requestStream(new PayloadImpl("REQUEST", "META")); - } - - @Test(timeout = 2_000L) - public void testSingle() throws InterruptedException { - ReactiveSocket requestHandler = mock(ReactiveSocket.class); - - when(requestHandler.requestStream(any())) - .thenReturn(Flowable.just(new PayloadImpl("RESPONSE", "METADATA"))); - - TransportServer.StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) - .start((setup, sendingSocket) -> { - Flowable.fromPublisher(sendingSocket.onClose()) - .subscribe(); - - return new DisabledLeaseAcceptingSocket(requestHandler); - }); - ReactiveSocket client = Single.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(server.getServerAddress()), - SetupProvider.keepAlive(KeepAliveProvider.never()) - .disableLease()) - .connect()) - .blockingGet(); - - Flowable.fromPublisher(client.requestStream(new PayloadImpl("REQUEST", "META"))) - .blockingSingle(); - - verify(requestHandler).requestStream(any()); - } - - @Test() - public void testZeroPayload() throws InterruptedException { - ReactiveSocket requestHandler = mock(ReactiveSocket.class); - - when(requestHandler.requestStream(any())) - .thenReturn(Flowable.just(PayloadImpl.EMPTY)); - - TransportServer.StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create()) - .start((setup, sendingSocket) -> { - Flowable.fromPublisher(sendingSocket.onClose()) - .subscribe(); - - return new DisabledLeaseAcceptingSocket(requestHandler); - }); - ReactiveSocket client = Single.fromPublisher(ReactiveSocketClient.create(TcpTransportClient.create(server.getServerAddress()), - SetupProvider.keepAlive(KeepAliveProvider.never()) - .disableLease()) - .connect()) - .blockingGet(); - - int dataSize = Flowable.fromPublisher(client.requestStream(new PayloadImpl("REQUEST", "META"))) - .map(p -> p.getData().remaining()) - .blockingSingle(); - - verify(requestHandler).requestStream(any()); - Assert.assertEquals(0, dataSize); - } -} diff --git a/reactivesocket-publishers/build.gradle b/reactivesocket-publishers/build.gradle deleted file mode 100644 index 4bf006ae3..000000000 --- a/reactivesocket-publishers/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -dependencies { - compile 'org.agrona:Agrona:0.5.4' -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/DefaultSubscriber.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/DefaultSubscriber.java deleted file mode 100644 index 928e7bc13..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/DefaultSubscriber.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DefaultSubscriber implements Subscriber { - - private static final Logger logger = LoggerFactory.getLogger(DefaultSubscriber.class); - - @SuppressWarnings("rawtypes") - private static final Subscriber defaultInstance = new DefaultSubscriber(); - - @Override - public void onSubscribe(Subscription s) { - s.request(Long.MAX_VALUE); - } - - @Override - public void onNext(T o) { - if (logger.isDebugEnabled()) { - logger.debug("Next emission reached the defaul subscriber. Item => " + o); - } - } - - @Override - public void onError(Throwable t) { - logger.error("Uncaught error emitted.", t); - } - - @Override - public void onComplete() { - - } - - @SuppressWarnings("unchecked") - public static Subscriber defaultInstance() { - return defaultInstance; - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/ExecutorServiceBasedScheduler.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/ExecutorServiceBasedScheduler.java deleted file mode 100644 index 8b2ca400a..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/ExecutorServiceBasedScheduler.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -public class ExecutorServiceBasedScheduler implements Scheduler { - - private static final ScheduledExecutorService globalExecutor; - - static { - globalExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r, "global-executor-scheduler"); - t.setDaemon(true); - return t; - } - }); - } - - private final ScheduledExecutorService executorService; - - public ExecutorServiceBasedScheduler() { - this(globalExecutor); - } - - public ExecutorServiceBasedScheduler(ScheduledExecutorService executorService) { - this.executorService = executorService; - } - - @Override - public Px timer(long timeout, TimeUnit unit) { - return subscriber -> { - AtomicReference> futureRef = new AtomicReference<>(); - final ValidatingSubscription subscription = - ValidatingSubscription.onCancel(subscriber, () -> { - ScheduledFuture scheduledFuture = futureRef.get(); - scheduledFuture.cancel(false); - }); - futureRef.set(executorService.schedule(() -> { - subscription.safeOnComplete(); - }, timeout, unit)); - subscriber.onSubscribe(subscription); - }; - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/Px.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/Px.java deleted file mode 100644 index 37b88c616..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/Px.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; -import io.reactivesocket.reactivestreams.extensions.internal.publishers.CachingPublisher; -import io.reactivesocket.reactivestreams.extensions.internal.publishers.ConcatPublisher; -import io.reactivesocket.reactivestreams.extensions.internal.publishers.DoOnEventPublisher; -import io.reactivesocket.reactivestreams.extensions.internal.publishers.SwitchToPublisher; -import io.reactivesocket.reactivestreams.extensions.internal.publishers.TimeoutPublisher; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; -import org.agrona.UnsafeAccess; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.LongConsumer; -import java.util.function.Supplier; - -/** - * Extension of the {@link Publisher} interface with useful methods to create and transform data - * - * @param - */ -public interface Px extends Publisher { - - /** - * A new {@code Px} that just emits the passed {@code item} and completes. - * - * @param item that the returned source will emit. - * @return New {@code Publisher} which just emits the passed {@code item}. - */ - static Px just(T item) { - return subscriber -> - subscriber.onSubscribe(new Subscription() { - boolean cancelled; - - @Override - public void request(long n) { - if (isCancelled()) { - return; - } - - if (n < 0) { - subscriber.onError(new IllegalArgumentException("n less than zero")); - } - - try { - subscriber.onNext(item); - subscriber.onComplete(); - } catch (Throwable t) { - subscriber.onError(t); - } - } - - private boolean isCancelled() { - UnsafeAccess.UNSAFE.loadFence(); - return cancelled; - } - - @Override - public void cancel() { - cancelled = true; - UnsafeAccess.UNSAFE.storeFence(); - } - }); - } - - /** - * A new {@code Px} that does not emit any item, just completes. The returned {@code Px} instance - * does not wait for {@link Subscription#request(long)} invocations before emitting the completion. - * - * @return New {@code Publisher} which just completes upon subscription. - */ - static Px empty() { - return subscriber -> { - try { - subscriber.onSubscribe(EMPTY_SUBSCRIPTION); - subscriber.onComplete(); - } catch (Throwable t) { - subscriber.onError(t); - } - }; - } - - /** - * Creates a new Px for you and passes in a subscriber - * @param subscriberConsumer closure that access a subscriber - * @param - * @return a new Px instance - */ - static Px create(Consumer> subscriberConsumer) { - return subscriberConsumer::accept; - } - - @FunctionalInterface - interface OnComplete extends Runnable { - } - - Subscription EMPTY_SUBSCRIPTION = new Subscription() { - @Override - public void request(long n) { - if (n < 0) { - throw new IllegalArgumentException("n must be greater than zero"); - } - - } - - @Override - public void cancel() { - } - }; - - /** - * A new {@code Px} that does not emit any item and never terminates. - * - * @return New {@code Publisher} which does not emit any item and never terminates. - */ - static Px never() { - return subscriber -> { - try { - subscriber.onSubscribe(ValidatingSubscription.empty(subscriber)); - } catch (Throwable t) { - subscriber.onError(t); - } - }; - } - - /** - * A new {@code Px} that does not emit any item, just emits the passed {@code t}. The returned {@code Px} instance - * does not wait for {@link Subscription#request(long)} invocations before emitting the error. - * - * @return New {@code Publisher} which just completes upon subscription. - */ - static Px error(Throwable t) { - return s -> { - s.onSubscribe(ValidatingSubscription.empty(s)); - s.onError(t); - }; - } - - /** - * Converts the passed {@code source} to an instance of {@code Px}.

    - * Returns the same object if {@code source} is already an instance of {@code Px} - * - * @param source {@code Publisher} to convert. - * @param Type for the source {@code Publisher} - * @return Source converted to {@code Px} - */ - static Px from(Publisher source) { - if (source instanceof Px) { - return (Px) source; - } else { - return source::subscribe; - } - } - - /** - * A new {@code Px} that does not emit any items, it just calls the {@link Runnable} - * passed to it. It either completes, or errors but doesn't emit an item. This will - * not emit until a {@link Subscription#request(long)} invocation. It will not - * emit if it is cancelled. - *

    - * If you receive an Exception convert it to a {@link RuntimeException} and throw it - * and it will be handle properly. - * - * @param onComplete called by your callback to single when it's complete - * @return New {@code Publisher} which completes when a Runnable is executed. - */ - static Px completable(Consumer onComplete) { - return subscriber -> { - try { - subscriber.onSubscribe(EMPTY_SUBSCRIPTION); - onComplete.accept(subscriber::onComplete); - } catch (Throwable t) { - subscriber.onError(t); - } - - }; - } - - /** - * Converts an eager {@code Publisher} to a lazy {@code Publisher}. - * - * @param supplierSource Supplier for the eager {@code Publisher}. - * @param Type of the source. - * @return Lazy {@code Publisher} delegating to the supplied source. - */ - static Px defer(Supplier> supplierSource) { - return s -> { - Publisher src = supplierSource.get(); - if (src == null) { - src = error(new NullPointerException("Deferred Publisher is null.")); - } - src.subscribe(s); - }; - } - - /** - * Concats two empty ({@code Void}) {@code Publisher}s into a single {@code Publisher}. - * - * @param first First {@code Publisher} to subscribe. - * @param second Second {@code Publisher} to subscribe only if the first did not terminate with an error. - * @return A new {@code Px} that concats the two passed {@code Publisher}s. - */ - static Px concatEmpty(Publisher first, Publisher second) { - return subscriber -> { - first.subscribe(Subscribers.create(subscriber::onSubscribe, subscriber::onNext, subscriber::onError, () -> { - second.subscribe(Subscribers.create(subscription -> { - // This is the second subscription which isn't driven by downstream subscriber. - // So, no onSubscriber callback will be coming here (alread done for first subscriber). - // As we are only dealing with empty (Void) sources, this doesn't break backpressure. - subscription.request(1); - }, subscriber::onNext, subscriber::onError, subscriber::onComplete, null)); - }, null)); - }; - } - - /** - * A special {@link #map(Function)} that blindly casts all items emitted from this {@code Px} to the passed class. - * - * @param toClass Target class to cast. - * @param Type after the cast. - * @return A new {@code Px} of the target type. - */ - default Px cast(Class toClass) { - return (Px) this; - } - - /** - * On emission of the first item from this {@code Px} switches to a new {@code Publisher} as provided by the - * {@code mapper}. Resulting {@code Px} will cancel the subscription to this {@code Px} after the emission of the - * first item and subscribe to the new {@code Publisher} returned by the {@code mapper}. - * - * @param mapper Function that provides the next {@code Publisher}. - * @param Type of the resulting {@code Px}. - * @return A new {@code Px} that switches to a new {@code Publisher} on emission of the first item. - */ - default Px switchTo(Function> mapper) { - return new SwitchToPublisher(this, mapper); - } - - default Px map(Function mapper) { - return subscriber -> - subscribe(new Subscriber() { - volatile boolean canEmit = true; - - @Override - public void onSubscribe(Subscription s) { - subscriber.onSubscribe(s); - } - - @Override - public void onNext(T t) { - if (canEmit) { - try { - R apply = mapper.apply(t); - subscriber.onNext(apply); - } catch (Throwable e) { - onError(e); - } - } - } - - @Override - public void onError(Throwable t) { - if (canEmit) { - canEmit = false; - subscriber.onError(t); - } - } - - @Override - public void onComplete() { - if (canEmit) { - canEmit = false; - subscriber.onComplete(); - } - } - }); - } - - /** - * Returns a publisher that ignores onNexts, and only emits onComplete, and onErrors. - */ - default Px ignore() { - return subscriber -> - subscribe(new Subscriber() { - Subscription subscription; - - @Override - public void onSubscribe(Subscription s) { - subscription = s; - subscriber.onSubscribe(s); - } - - @Override - public void onNext(T t) { - } - - @Override - public void onError(Throwable t) { - subscription.cancel(); - subscriber.onError(t); - } - - @Override - public void onComplete() { - subscriber.onComplete(); - } - }); - } - - /** - * Caches the first item emitted and completes - * - * @return Publishers that caches one item - */ - default Px cacheOne() { - return new CachingPublisher<>(this); - } - - default Px emitOnCancel(Supplier onCancel) { - return emitOnCancelOrError(onCancel, null); - } - - default Px emitOnCancelOrError(Supplier onCancel, Function onError) { - return subscriber -> - subscriber.onSubscribe(new Subscription() { - boolean started; - Subscription inner; - boolean cancelled; - - @Override - public void request(long n) { - if (n < 0) { - subscriber.onError(new IllegalStateException("n is less than zero")); - } - - boolean start = false; - synchronized (this) { - if (!started) { - started = true; - start = true; - } - } - - if (start) { - subscribe(new Subscriber() { - @Override - public void onSubscribe(Subscription s) { - inner = s; - } - - @Override - public void onNext(T t) { - subscriber.onNext(t); - } - - @Override - public void onError(Throwable t) { - synchronized (this) { - if (cancelled) { - return; - } - } - - if (onError != null) { - T apply = onError.apply(t); - subscriber.onNext(apply); - subscriber.onComplete(); - return; - } - - subscriber.onError(t); - } - - @Override - public void onComplete() { - subscriber.onComplete(); - } - }); - } - - if (inner != null) { - inner.request(n); - } - } - - @Override - public void cancel() { - synchronized (this) { - if (cancelled) { - return; - } - - cancelled = true; - } - if (inner != null) { - inner.cancel(); - } - - subscriber.onNext(onCancel.get()); - subscriber.onComplete(); - } - }); - } - - default Px doOnSubscribe(Consumer doOnSubscribe) { - return DoOnEventPublisher.onSubscribe(this, doOnSubscribe); - } - - default Px doOnRequest(LongConsumer doOnRequest) { - return DoOnEventPublisher.onRequest(this, doOnRequest); - } - - default Px doOnCancel(Runnable doOnCancel) { - return DoOnEventPublisher.onCancel(this, doOnCancel); - } - - default Px doOnNext(Consumer doOnNext) { - return DoOnEventPublisher.onNext(this, doOnNext); - } - - default Px doOnError(Consumer doOnError) { - return DoOnEventPublisher.onError(this, doOnError); - } - - default Px doOnComplete(Runnable doOnComplete) { - return DoOnEventPublisher.onComplete(this, doOnComplete); - } - - default Px doOnCompleteOrError(Runnable doOnComplete, Consumer doOnError) { - return DoOnEventPublisher.onCompleteOrError(this, doOnComplete, doOnError); - } - - default Px doOnTerminate(Runnable doOnTerminate) { - return DoOnEventPublisher.onTerminate(this, doOnTerminate); - } - - default Px timeout(long timeout, TimeUnit unit, Scheduler scheduler) { - return new TimeoutPublisher<>(this, timeout, unit, scheduler); - } - - default Px concatWith(Publisher concatSource) { - return new ConcatPublisher<>(this, concatSource); - } - - @SuppressWarnings("unchecked") - default void subscribe() { - subscribe(DefaultSubscriber.defaultInstance()); - } - -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/Scheduler.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/Scheduler.java deleted file mode 100644 index 7beef367b..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/Scheduler.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import org.reactivestreams.Publisher; - -import java.util.concurrent.TimeUnit; - -/** - * A contract used to schedule tasks in future. - */ -public interface Scheduler { - - /** - * A single tick timer which returns a {@code Publisher} that completes when the passed {@code time} is elapsed. - * - * @param time Time at which the timer will tick. - * @param unit {@code TimeUnit} for the time. - * - * @return A {@code Publisher} that completes when the time is elapsed. - */ - Publisher timer(long time, TimeUnit unit); - -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/TestScheduler.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/TestScheduler.java deleted file mode 100644 index 392833df7..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/TestScheduler.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import org.reactivestreams.Publisher; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; - -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -public class TestScheduler implements Scheduler { - - private long time; - - private final Queue queue = new PriorityQueue(11); - - @Override - public Publisher timer(long time, TimeUnit unit) { - return s -> { - ValidatingSubscription sub = ValidatingSubscription.empty(s); - queue.add(new TimedAction(this.time + unit.toNanos(time), sub)); - s.onSubscribe(sub); - }; - } - - /** - * Moves the Scheduler's clock forward by a specified amount of time. - * - * @param delayTime the amount of time to move the Scheduler's clock forward - * @param unit the units of time that {@code delayTime} is expressed in - */ - public void advanceTimeBy(long delayTime, TimeUnit unit) { - triggerActionsForNanos(time + unit.toNanos(delayTime)); - } - - private void triggerActionsForNanos(long targetTimeNanos) { - while (!queue.isEmpty()) { - TimedAction current = queue.peek(); - if (current.timeNanos > targetTimeNanos) { - break; - } - time = current.timeNanos; - queue.remove(); - - // Only execute if active - if (current.subscription.isActive()) { - current.subscription.safeOnComplete(); - } - } - time = targetTimeNanos; - } - - private static class TimedAction implements Comparable { - - private static final AtomicLong counter = new AtomicLong(); - private final long timeNanos; - private final ValidatingSubscription subscription; - private final long count = counter.incrementAndGet(); // for differentiating tasks at same timeNanos - - private TimedAction(long timeNanos, ValidatingSubscription subscription) { - this.timeNanos = timeNanos; - this.subscription = subscription; - } - - @Override - public String toString() { - return String.format("TimedAction(timeNanos = %d)", timeNanos); - } - - @Override - public int compareTo(TimedAction o) { - if (timeNanos == o.timeNanos) { - return Long.compare(count, o.count); - } - return Long.compare(timeNanos, o.timeNanos); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof TimedAction)) { - return false; - } - - TimedAction that = (TimedAction) o; - - if (timeNanos != that.timeNanos) { - return false; - } - if (count != that.count) { - return false; - } - if (subscription != null? !subscription.equals(that.subscription) : that.subscription != null) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = (int) (timeNanos ^ timeNanos >>> 32); - result = 31 * result + (subscription != null? subscription.hashCode() : 0); - result = 31 * result + (int) (count ^ count >>> 32); - return result; - } - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/Cancellable.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/Cancellable.java deleted file mode 100644 index 464ddd5ca..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/Cancellable.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal; - -/** - * A contract to cancel a running process. - */ -public interface Cancellable { - - /** - * Cancels the associated process. This call is idempotent. - */ - void cancel(); - - /** - * Asserts whether the associated process is cancelled as a result of a prior {@link #cancel()} call. - * - * @return {@code true} if the associated process is cancelled. - */ - boolean isCancelled(); -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/CancellableImpl.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/CancellableImpl.java deleted file mode 100644 index 2ec1696db..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/CancellableImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal; - -public class CancellableImpl implements Cancellable { - - private boolean cancelled; - - @Override - public void cancel() { - synchronized (this) { - cancelled = true; - } - onCancel(); - } - - @Override - public synchronized boolean isCancelled() { - return cancelled; - } - - protected void onCancel() { - // No Op. - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/EmptySubject.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/EmptySubject.java deleted file mode 100644 index abbf65f10..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/EmptySubject.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal; - -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A {@code Publisher} implementation that can only send a termination signal. - */ -public class EmptySubject implements Publisher { - - private static final Logger logger = LoggerFactory.getLogger(EmptySubject.class); - - private boolean terminated; - private Throwable optionalError; - private final List> earlySubscribers = new ArrayList<>(); - - @Override - public void subscribe(Subscriber subscriber) { - subscriber.onSubscribe(new Subscription() { - @Override - public void request(long n) { - - } - - @Override - public void cancel() { - - } - }); - boolean _completed = false; - final Throwable _error; - synchronized (this) { - if (terminated) { - _completed = true; - } else { - earlySubscribers.add(subscriber); - } - _error = optionalError; - } - - if (_completed) { - if (_error != null) { - subscriber.onError(_error); - } else { - subscriber.onComplete(); - } - } - } - - public void onComplete() { - sendSignalIfRequired(null); - } - - public void onError(Throwable throwable) { - sendSignalIfRequired(throwable); - } - - private void sendSignalIfRequired(Throwable optionalError) { - List> subs = Collections.emptyList(); - synchronized (this) { - if (!terminated) { - terminated = true; - subs = new ArrayList<>(earlySubscribers); - earlySubscribers.clear(); - this.optionalError = optionalError; - } - } - - for (Subscriber sub : subs) { - try { - if (optionalError != null) { - sub.onError(optionalError); - } else { - sub.onComplete(); - } - } catch (Throwable e) { - logger.error("Error while sending terminal notification. Ignoring the error.", e); - } - } - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/SerializedSubscription.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/SerializedSubscription.java deleted file mode 100644 index 90b164fa5..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/SerializedSubscription.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal; - -import org.reactivestreams.Subscription; - -/** - * An implementation of {@link Subscription} that allows concatenating multiple subscriptions and takes care of - * cancelling the previous {@code Subscription} when next {@code Subscription} is provided and passed the remaining - * requests to the next {@code Subscription}.

    - * It is mandated to use {@link #onItemReceived()} on every item received by the associated {@code Subscriber} to - * correctly manage flow control. - */ -public class SerializedSubscription implements Subscription { - - private int requested; - private Subscription current; - private boolean cancelled; - - public SerializedSubscription(Subscription first) { - current = first; - } - - @Override - public void request(long n) { - Subscription subscription; - synchronized (this) { - requested = FlowControlHelper.incrementRequestN(requested, n); - subscription = current; - } - if (subscription != null) { - subscription.request(n); - } - } - - @Override - public void cancel() { - Subscription subscription; - synchronized (this) { - subscription = current; - cancelled = true; - } - subscription.cancel(); - } - - public void cancelCurrent() { - Subscription subscription; - synchronized (this) { - subscription = current; - } - subscription.cancel(); - } - - public synchronized void onItemReceived() { - requested = Math.max(0, requested - 1); - } - - public void replaceSubscription(Subscription next) { - Subscription prev; - int _pendingRequested; - boolean _cancelled; - synchronized (this) { - _pendingRequested = requested; - _cancelled = cancelled; - requested = 0; // Reset for next requestN - prev = current; - current = next; - } - prev.cancel(); - if (_cancelled) { - next.cancel(); - } else if (_pendingRequested > 0) { - next.request(_pendingRequested); - } - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/package-info.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/package-info.java deleted file mode 100644 index 2fb12b3b4..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Internal package and must not be used outside this project. There are no guarantees for API compatibility. - */ -package io.reactivesocket.reactivestreams.extensions.internal; diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/processors/ConnectableUnicastProcessor.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/processors/ConnectableUnicastProcessor.java deleted file mode 100644 index d7928910c..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/processors/ConnectableUnicastProcessor.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.processors; - -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.FlowControlHelper; -import org.reactivestreams.Processor; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -/** - * {@link ConnectableUnicastProcessor} is a processor that doesn't start emitting items until it's been subscribed too, and - * has subscribed to a Publisher. It has a method requestMore that lets you limit the number of items being requested in addition - * to the items being requested from the subscriber. - */ -public class ConnectableUnicastProcessor implements Processor, Px, Subscription { - private Subscription subscription; - - private long destinationRequested; - private long externallyRequested; - private long actuallyRequested; - - private Subscriber destination; - - private boolean complete; - private boolean erred; - private boolean cancelled; - private boolean stated; - - private Throwable error; - - private Runnable lazy; - - @Override - public void subscribe(Subscriber destination) { - this.lazy = () -> { - if (this.destination != null) { - destination.onError(new IllegalStateException("Only single Subscriber supported")); - - } else { - if (error != null) { - destination.onError(error); - return; - } - this.destination = destination; - destination.onSubscribe(new Subscription() { - @Override - public void request(long n) { - if (canEmit()) { - synchronized (ConnectableUnicastProcessor.this) { - destinationRequested = FlowControlHelper.incrementRequestN(destinationRequested, n); - } - tryRequestingMore(); - } - } - - @Override - public void cancel() { - synchronized (ConnectableUnicastProcessor.this) { - cancelled = true; - } - } - }); - } - }; - - start(); - } - - private void start() { - boolean start = false; - synchronized (this) { - if (subscription != null && lazy != null) { - start = true; - } - } - - if (start) { - lazy.run(); - } - } - - @Override - public void onSubscribe(Subscription s) { - synchronized (this) { - subscription = s; - } - start(); - - tryRequestingMore(); - } - - @Override - public void onNext(T t) { - if (canEmit()) { - destination.onNext(t); - } - } - - @Override - public void onError(Throwable t) { - if (canEmit()) { - synchronized (this) { - erred = true; - error = t; - } - - destination.onError(t); - } - } - - @Override - public void onComplete() { - if (canEmit()) { - synchronized (this) { - complete = true; - } - destination.onComplete(); - } - } - - private synchronized boolean canEmit() { - return !complete && !erred && !cancelled; - } - - @Override - public void cancel() { - synchronized (this) { - cancelled = true; - } - - if (subscription != null) { - subscription.cancel(); - } - } - - /** - * Starts the connectable processor with an intial request n - */ - public void start(long request) { - synchronized (this) { - if (stated) { - return; - } - - stated = true; - } - request(request); - } - - @Override - public void request(long request) { - if (canEmit()) { - synchronized (this) { - externallyRequested = FlowControlHelper.incrementRequestN(externallyRequested, request); - } - tryRequestingMore(); - } - } - - private void tryRequestingMore() { - long diff; - synchronized (this) { - long minRequested = Math.min(externallyRequested, destinationRequested); - diff = minRequested - actuallyRequested; - actuallyRequested += diff; - } - if (subscription != null && diff > 0) { - subscription.request(diff); - } - - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/CachingPublisher.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/CachingPublisher.java deleted file mode 100644 index 9d133d5a2..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/CachingPublisher.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivesocket.reactivestreams.extensions.Px; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * - */ -public class CachingPublisher implements Px { - private T cached; - private Throwable error; - - private Publisher source; - - private boolean subscribed; - - private CopyOnWriteArrayList> subscribers = new CopyOnWriteArrayList<>(); - - public CachingPublisher(Publisher source) { - this.source = source; - } - - @Override - public void subscribe(Subscriber s) { - synchronized (this) { - if (cached != null || error != null) { - subscribers.add(s); - if (!subscribed) { - subscribed = true; - source - .subscribe(new Subscriber() { - @Override - public void onSubscribe(Subscription s) { - s.request(1); - } - - @Override - public void onNext(T t) { - synchronized (CachingPublisher.this) { - cached = t; - complete(); - } - } - - @Override - public void onError(Throwable t) { - synchronized (CachingPublisher.this) { - error = t; - complete(); - } - } - - void complete() { - for (Subscriber s : subscribers) { - if (error != null) { - s.onError(error); - } else { - s.onNext(cached); - s.onComplete(); - } - } - } - - @Override - public void onComplete() { - synchronized (CachingPublisher.this) { - if (error == null && cached == null) { - s.onComplete(); - } - } - } - }); - } - - } else { - s.onSubscribe(new Subscription() { - boolean cancelled; - - @Override - public synchronized void request(long n) { - if (n > 1 && !cancelled) { - if (cached == null) { - s.onError(error); - } else { - s.onNext(cached); - s.onComplete(); - } - } - } - - @Override - public synchronized void cancel() { - cancelled = true; - } - }); - } - } - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/ConcatPublisher.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/ConcatPublisher.java deleted file mode 100644 index 00e37cfbf..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/ConcatPublisher.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivesocket.reactivestreams.extensions.internal.SerializedSubscription; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import io.reactivesocket.reactivestreams.extensions.Px; - -public final class ConcatPublisher implements Px { - - private final Publisher first; - private final Publisher second; - - public ConcatPublisher(Publisher first, Publisher second) { - this.first = first; - this.second = second; - } - - @Override - public void subscribe(Subscriber destination) { - first.subscribe(new Subscriber() { - private SerializedSubscription subscription; - - @Override - public void onSubscribe(Subscription s) { - subscription = new SerializedSubscription(s); - destination.onSubscribe(subscription); - } - - @Override - public void onNext(T t) { - subscription.onItemReceived(); - destination.onNext(t); - } - - @Override - public void onError(Throwable t) { - destination.onError(t); - } - - @Override - public void onComplete() { - second.subscribe(new Subscriber() { - @Override - public void onSubscribe(Subscription s) { - subscription.replaceSubscription(s); - } - - @Override - public void onNext(T t) { - destination.onNext(t); - } - - @Override - public void onError(Throwable t) { - destination.onError(t); - } - - @Override - public void onComplete() { - destination.onComplete(); - } - }); - } - }); - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/DoOnEventPublisher.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/DoOnEventPublisher.java deleted file mode 100644 index 8e9afd69c..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/DoOnEventPublisher.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivesocket.reactivestreams.extensions.Px; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.LongConsumer; - -public final class DoOnEventPublisher implements Px { - private final Runnable doOnCancel; - private final Consumer doOnNext; - private final Consumer doOnError; - private final Runnable doOnComplete; - private final Consumer doOnSubscribe; - private final LongConsumer doOnRequest; - private final Publisher source; - - private DoOnEventPublisher(Px source, - Consumer doOnSubscribe, - Runnable doOnCancel, - Consumer doOnNext, - Consumer doOnError, - Runnable doOnComplete, - LongConsumer doOnRequest) { - Objects.requireNonNull(source, "source subscriber must not be null"); - this.source = source; - this.doOnSubscribe = doOnSubscribe; - this.doOnCancel = doOnCancel; - this.doOnRequest = doOnRequest; - this.doOnNext = doOnNext; - this.doOnError = doOnError; - this.doOnComplete = doOnComplete; - } - - @Override - public void subscribe(Subscriber subscriber) { - source.subscribe(new Subscriber() { - @Override - public void onSubscribe(Subscription s) { - if (doOnSubscribe != null) { - doOnSubscribe.accept(s); - } - - if (null == doOnRequest && null == doOnCancel) { - subscriber.onSubscribe(s); - } else { - subscriber.onSubscribe(decorateSubscription(s)); - } - } - - @Override - public void onNext(T t) { - if (doOnNext != null) { - doOnNext.accept(t); - } - subscriber.onNext(t); - } - - @Override - public void onError(Throwable t) { - if (doOnError != null) { - doOnError.accept(t); - } - subscriber.onError(t); - } - - @Override - public void onComplete() { - if (doOnComplete != null) { - doOnComplete.run(); - } - subscriber.onComplete(); - } - }); - } - - private Subscription decorateSubscription(Subscription subscription) { - return new Subscription() { - @Override - public void request(long n) { - if (doOnRequest != null) { - doOnRequest.accept(n); - } - subscription.request(n); - } - - @Override - public void cancel() { - if (doOnCancel != null) { - doOnCancel.run(); - } - subscription.cancel(); - } - }; - } - - public static Px onNext(Px source, Consumer doOnNext) { - return new DoOnEventPublisher<>(source, null, null, doOnNext::accept, null, null, null); - } - - public static Px onError(Px source, Consumer doOnError) { - return new DoOnEventPublisher<>(source, null, null, null, doOnError, null, null); - } - - public static Px onComplete(Px source, Runnable doOnComplete) { - return new DoOnEventPublisher<>(source, null, null, null, null, doOnComplete, null); - } - - public static Px onCompleteOrError(Px source, Runnable doOnComplete, Consumer doOnError) { - return new DoOnEventPublisher<>(source, null, null, null, doOnError, doOnComplete, null); - } - - public static Px onTerminate(Px source, Runnable doOnTerminate) { - return new DoOnEventPublisher<>(source, null, null, null, throwable -> doOnTerminate.run(), - doOnTerminate, null); - } - - public static Px onCompleteOrErrorOrCancel(Px source, Runnable doOnCancel, Runnable doOnComplete, Consumer doOnError) { - return new DoOnEventPublisher<>(source, null, doOnCancel, null, doOnError, - doOnComplete, null); - } - - public static Px onSubscribe(Px source, Consumer doOnSubscribe) { - return new DoOnEventPublisher(source, doOnSubscribe, null, null, null, null, null); - } - - public static Px onCancel(Px source, Runnable doOnCancel) { - return new DoOnEventPublisher<>(source, null, doOnCancel, null, null, null, null); - } - - public static Px onRequest(Px source, LongConsumer doOnRequest) { - return new DoOnEventPublisher<>(source, null, null, null, null, null, doOnRequest); - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/InstrumentingPublisher.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/InstrumentingPublisher.java deleted file mode 100644 index 9ac8a4aad..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/InstrumentingPublisher.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.Scheduler; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.CancellableSubscriber; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; - -/** - * A {@link Px} instance that facilitates usecases that involve creating a state on subscription and using it for all - * subsequent callbacks. eg: Capturing timings from subscription to termination. - * - * @param Type of objects emitted by the publisher. - * @param State to be created on subscription. - */ -public final class InstrumentingPublisher implements Px { - - private final Publisher source; - private final Function, X> stateFactory; - private final BiConsumer onError; - private final Consumer onComplete; - private final Consumer onCancel; - private final BiConsumer onNext; - - public InstrumentingPublisher(Publisher source, Function, X> stateFactory, - BiConsumer onError, Consumer onComplete, Consumer onCancel, - BiConsumer onNext) { - this.source = source; - this.stateFactory = stateFactory; - this.onError = onError; - this.onComplete = onComplete; - this.onCancel = onCancel; - this.onNext = onNext; - } - - @Override - public void subscribe(Subscriber subscriber) { - source.subscribe(new Subscriber() { - - private volatile X state; - private volatile boolean emit = true; - - @Override - public void onSubscribe(Subscription s) { - state = stateFactory.apply(subscriber); - if (null != onCancel) { - subscriber.onSubscribe(new Subscription() { - @Override - public void request(long n) { - s.request(n); - } - - @Override - public void cancel() { - if (emit) { - onCancel.accept(state); - } - emit = false; - s.cancel(); - } - }); - } else { - subscriber.onSubscribe(s); - } - } - - @Override - public void onNext(T t) { - if (emit && null != onNext) { - onNext.accept(state, t); - } - subscriber.onNext(t); - } - - @Override - public void onError(Throwable t) { - if (emit && null != onError) { - onError.accept(state, t); - } - emit = false; - subscriber.onError(t); - } - - @Override - public void onComplete() { - if (emit && null != onComplete) { - onComplete.accept(state); - } - emit = false; - subscriber.onComplete(); - } - }); - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SwitchToPublisher.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SwitchToPublisher.java deleted file mode 100644 index c28f24feb..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SwitchToPublisher.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.SerializedSubscription; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.function.Function; - -public final class SwitchToPublisher implements Px { - - private final Px source; - private final Function> switchProvider; - - public SwitchToPublisher(Px source, Function> switchProvider) { - this.source = source; - this.switchProvider = switchProvider; - } - - @Override - public void subscribe(Subscriber target) { - source.subscribe(new Subscriber() { - - private SerializedSubscription subscription; - private boolean switched; - - @Override - public void onSubscribe(Subscription s) { - synchronized (this) { - if (subscription != null) { - s.cancel(); - return; - } - subscription = new SerializedSubscription(s); - } - target.onSubscribe(subscription); - } - - @Override - public void onNext(T t) { - // Do Not notify SerializedSubscription of the item as it is not emitted to the target. - final boolean switchNow; - synchronized (this) { - switchNow = !switched; - switched = true; - } - if (switchNow) { - subscription.cancelCurrent(); - Publisher next = switchProvider.apply(t); - next.subscribe(new Subscriber() { - @Override - public void onSubscribe(Subscription s) { - subscription.replaceSubscription(s); - } - - @Override - public void onNext(R r) { - subscription.onItemReceived(); - target.onNext(r); - } - - @Override - public void onError(Throwable t) { - target.onError(t); - } - - @Override - public void onComplete() { - target.onComplete(); - } - }); - } - } - - @Override - public void onError(Throwable t) { - boolean _switched; - synchronized (this) { - _switched = switched; - if (!switched) { - switched = true;// Handle cases when onNext arrives after terminate. - } - } - if (!_switched) { - // Empty source completes the target subscriber. - target.onError(t); - } - } - - @Override - public void onComplete() { - boolean _switched; - synchronized (this) { - _switched = switched; - if (!switched) { - switched = true;// Handle cases when onNext arrives after complete. - } - } - if (!_switched) { - // Empty source completes the target subscriber. - target.onComplete(); - } - } - }); - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/TimeoutPublisher.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/TimeoutPublisher.java deleted file mode 100644 index 160d0250d..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/TimeoutPublisher.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.Scheduler; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.CancellableSubscriber; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public final class TimeoutPublisher implements Px { - - @SuppressWarnings("ThrowableInstanceNeverThrown") - private static final TimeoutException timeoutException = new TimeoutException() { - private static final long serialVersionUID = 6195545973881750858L; - - @Override - public synchronized Throwable fillInStackTrace() { - return this; - } - }; - - private final Publisher child; - private final Scheduler scheduler; - private final long timeout; - private final TimeUnit unit; - - public TimeoutPublisher(Publisher child, long timeout, TimeUnit unit, Scheduler scheduler) { - this.child = child; - this.timeout = timeout; - this.unit = unit; - this.scheduler = scheduler; - } - - @Override - public void subscribe(Subscriber subscriber) { - Runnable onTimeout = () -> subscriber.onError(timeoutException); - CancellableSubscriber timeoutSub = Subscribers.create(null, null, throwable -> onTimeout.run(), - onTimeout, null); - Runnable cancelTimeout = () -> timeoutSub.cancel(); - Subscriber sourceSub = Subscribers.create(subscription -> subscriber.onSubscribe(subscription), - t -> { - cancelTimeout.run(); - subscriber.onNext(t); - }, - throwable -> { - cancelTimeout.run(); - subscriber.onError(throwable); - }, - () -> { - cancelTimeout.run(); - subscriber.onComplete(); - }, - cancelTimeout); - child.subscribe(sourceSub); - scheduler.timer(timeout, unit).subscribe(timeoutSub); - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/CancellableSubscriber.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/CancellableSubscriber.java deleted file mode 100644 index 7ee9772b8..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/CancellableSubscriber.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.subscribers; - -import io.reactivesocket.reactivestreams.extensions.internal.Cancellable; -import org.reactivestreams.Subscriber; - -public interface CancellableSubscriber extends Subscriber, Cancellable { - void request(long n); -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/CancellableSubscriberImpl.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/CancellableSubscriberImpl.java deleted file mode 100644 index 9df74aff5..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/CancellableSubscriberImpl.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.subscribers; - -import org.reactivestreams.Subscription; - -import java.util.function.Consumer; - -public final class CancellableSubscriberImpl implements CancellableSubscriber { - - private final Runnable onCancel; - private final Consumer doOnNext; - private final Consumer doOnError; - private final Runnable doOnComplete; - private final Consumer doOnSubscribe; - private Subscription subscription; - private boolean done; - private boolean cancelled; - private boolean subscribed; - - public CancellableSubscriberImpl( - Consumer doOnSubscribe, - Runnable doOnCancel, - Consumer doOnNext, - Consumer doOnError, - Runnable doOnComplete) { - this.doOnSubscribe = doOnSubscribe; - onCancel = doOnCancel; - this.doOnNext = doOnNext; - this.doOnError = doOnError; - this.doOnComplete = doOnComplete; - } - - @SuppressWarnings("unchecked") - public CancellableSubscriberImpl() { - this(null, null, null, null, null); - } - - @Override - public void request(long n) { - subscription.request(n); - } - - @Override - public void onSubscribe(Subscription s) { - boolean _cancel = false; - synchronized (this) { - if (!subscribed) { - subscribed = true; - this.subscription = s; - if (cancelled) { - _cancel = true; - } - } else { - _cancel = true; - } - } - - if (_cancel) { - s.cancel(); - } else if (doOnSubscribe != null) { - doOnSubscribe.accept(s); - } - } - - @Override - public void cancel() { - boolean _cancel = false; - synchronized (this) { - if (subscription != null && !cancelled) { - _cancel = true; - } - cancelled = true; - done = true; - } - - if (_cancel) { - unsafeCancel(); - } - } - - @Override - public synchronized boolean isCancelled() { - return cancelled; - } - - @Override - public void onNext(T t) { - if (doOnNext != null && canEmit()) { - doOnNext.accept(t); - } - } - - @Override - public void onError(Throwable t) { - if (doOnError != null && !terminate()) { - doOnError.accept(t); - } - } - - @Override - public void onComplete() { - if (doOnComplete != null && !terminate()) { - doOnComplete.run(); - } - } - - private synchronized boolean terminate() { - boolean oldDone = done; - done = true; - return oldDone; - } - - private synchronized boolean canEmit() { - return !done; - } - - private void unsafeCancel() { - subscription.cancel(); - if (onCancel != null) { - onCancel.run(); - } - } -} diff --git a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/Subscribers.java b/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/Subscribers.java deleted file mode 100644 index 3852cef85..000000000 --- a/reactivesocket-publishers/src/main/java/io/reactivesocket/reactivestreams/extensions/internal/subscribers/Subscribers.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.subscribers; - -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.function.Consumer; - -/** - * A factory to create instances of {@link Subscriber} that follow reactive stream specifications. - */ -public final class Subscribers { - - private Subscribers() { - } - - /** - * Creates a new {@code Subscriber} instance that ignores all invocations but follows reactive streams specfication. - * - * @param Type parameter - * - * @return A new {@code Subscriber} instance. - */ - public static CancellableSubscriber empty() { - return new CancellableSubscriberImpl(); - } - - /** - * Creates a new {@code Subscriber} instance that ignores all invocations other than - * {@link Subscriber#onSubscribe(Subscription)} but follows reactive streams specfication. - * - * @param doOnSubscribe Callback for {@link Subscriber#onSubscribe(Subscription)} - * @param Type parameter - * - * @return A new {@code Subscriber} instance. - */ - public static CancellableSubscriber doOnSubscribe(Consumer doOnSubscribe) { - return new CancellableSubscriberImpl<>(doOnSubscribe, null, null, null, - null); - } - - /** - * Creates a new {@code Subscriber} instance that ignores all invocations other than - * {@link Subscriber#onSubscribe(Subscription)} and {@link Subscription#cancel()} but follows reactive - * streams specfication. - * - * @param doOnSubscribe Callback for {@link Subscriber#onSubscribe(Subscription)} - * @param doOnCancel Callback for {@link Subscription#cancel()} - * @param Type parameter - * - * @return A new {@code Subscriber} instance. - */ - public static CancellableSubscriber create(Consumer doOnSubscribe, Runnable doOnCancel) { - return new CancellableSubscriberImpl(doOnSubscribe, doOnCancel, null, null, - null); - } - - /** - * Creates a new {@code Subscriber} instance that listens to callbacks for all methods and follows reactive streams - * specfication. - * - * @param doOnSubscribe Callback for {@link Subscriber#onSubscribe(Subscription)} - * @param doOnNext Callback for {@link Subscriber#onNext(Object)} - * @param doOnError Callback for {@link Subscriber#onError(Throwable)} - * @param doOnComplete Callback for {@link Subscriber#onComplete()} - * @param doOnCancel Callback for {@link Subscription#cancel()} - * @param Type parameter - * - * @return A new {@code Subscriber} instance. - */ - public static CancellableSubscriber create(Consumer doOnSubscribe, Consumer doOnNext, - Consumer doOnError, Runnable doOnComplete, - Runnable doOnCancel) { - return new CancellableSubscriberImpl<>(doOnSubscribe, doOnCancel, doOnNext, doOnError, doOnComplete); - } - - /** - * Creates a new {@code Subscriber} instance that ignores all invocations other than - * {@link Subscriber#onError(Throwable)} but follows reactive streams specfication. - * - * @param doOnError Callback for {@link Subscriber#onError(Throwable)} - * @param Type parameter - * - * @return A new {@code Subscriber} instance. - */ - public static CancellableSubscriber doOnError(Consumer doOnError) { - return new CancellableSubscriberImpl(null, null, null, doOnError, - null); - } - - /** - * Creates a new {@code Subscriber} instance that ignores all invocations other than - * {@link Subscriber#onError(Throwable)} and {@link Subscriber#onComplete()} but follows reactive streams - * specfication. - * - * @param doOnTerminate Callback after the source finished with error or success. - * @param Type parameter - * - * @return A new {@code Subscriber} instance. - */ - public static CancellableSubscriber doOnTerminate(Runnable doOnTerminate) { - return new CancellableSubscriberImpl(null, null, null, - throwable -> doOnTerminate.run(), doOnTerminate); - } - - /** - * Creates a new {@code Subscriber} instance that ignores all invocations other than - * {@link Subscriber#onError(Throwable)}, {@link Subscriber#onComplete()} and - * {@link Subscription#cancel()} but follows reactive streams specfication. - * - * @param doFinally Callback invoked exactly once, after the source finished with error, success or cancelled. - * @param Type parameter - * - * @return A new {@code Subscriber} instance. - */ - public static CancellableSubscriber cleanup(Runnable doFinally) { - Runnable runOnce = new Runnable() { - private boolean done; - - @Override - public void run() { - synchronized (this) { - if (done) { - return; - } - done = true; - } - doFinally.run(); - } - }; - - return new CancellableSubscriberImpl(null, runOnce, null, - throwable -> runOnce.run(), runOnce); - } -} diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/ConcatTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/ConcatTest.java deleted file mode 100644 index ec21851e2..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/ConcatTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivex.subscribers.TestSubscriber; -import org.junit.Test; - -public class ConcatTest { - - @Test - public void testConcatNoError() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.concatEmpty(Px.empty(), Px.empty()).subscribe(subscriber); - subscriber.assertComplete().assertNoErrors().assertNoValues(); - } - - @Test - public void testConcatFirstNeverCompletes() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.concatEmpty(Px.never(), Px.empty()).subscribe(subscriber); - subscriber.assertNotComplete().assertNoErrors().assertNoValues(); - } - - @Test - public void testConcatSecondNeverCompletes() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.concatEmpty(Px.empty(), Px.never()).subscribe(subscriber); - subscriber.assertNotComplete().assertNoErrors().assertNoValues(); - } - - @Test - public void testConcatFirstError() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.concatEmpty(Px.error(new NullPointerException("Deliberate exception")), - Px.never()) - .subscribe(subscriber); - - subscriber.assertNotComplete().assertError(NullPointerException.class).assertNoValues(); - } - - @Test - public void testConcatSecondError() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.concatEmpty(Px.empty(), - Px.error(new NullPointerException("Deliberate exception"))) - .subscribe(subscriber); - - subscriber.assertNotComplete().assertError(NullPointerException.class).assertNoValues(); - } - - @Test - public void testConcatBothError() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.concatEmpty(Px.error(new NullPointerException("Deliberate exception")), - Px.error(new IllegalArgumentException("Deliberate exception"))) - .subscribe(subscriber); - - subscriber.assertNotComplete().assertError(NullPointerException.class).assertNoValues(); - } - - @Test - public void testConcatNoRequested() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(0); - Px.concatEmpty(Px.empty(), Px.empty()) - .subscribe(subscriber); - - subscriber.assertComplete().assertNoErrors().assertNoValues(); - } -} diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/ExecutorServiceBasedSchedulerTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/ExecutorServiceBasedSchedulerTest.java deleted file mode 100644 index 3167d2d54..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/ExecutorServiceBasedSchedulerTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import io.reactivex.subscribers.TestSubscriber; -import org.junit.Test; - -import java.util.concurrent.TimeUnit; - -public class ExecutorServiceBasedSchedulerTest { - - @Test(timeout = 10000) - public void testTimer() throws Exception { - ExecutorServiceBasedScheduler scheduler = new ExecutorServiceBasedScheduler(); - TestSubscriber testSub = TestSubscriber.create(); - scheduler.timer(1, TimeUnit.MILLISECONDS).subscribe(testSub); - testSub.await().assertNoErrors(); - } - - @Test(timeout = 10000) - public void testCancelTimer() throws Exception { - ExecutorServiceBasedScheduler scheduler = new ExecutorServiceBasedScheduler(); - TestSubscriber testSub = TestSubscriber.create(); - scheduler.timer(1, TimeUnit.SECONDS).subscribe(testSub); - testSub.assertNotTerminated(); - testSub.cancel(); - testSub.assertNotTerminated(); - } -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/PxTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/PxTest.java deleted file mode 100644 index c1ceea304..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/PxTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import io.reactivex.Flowable; -import io.reactivex.subscribers.TestSubscriber; -import org.junit.Test; - -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class PxTest { - - @Test(timeout = 2000) - public void testMap() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(); - Px.from(Flowable.range(1, 5)) - .map(String::valueOf) - .subscribe(testSubscriber); - - final List expected = Stream.of(1, 2, 3, 4, 5).map(String::valueOf).collect(Collectors.toList()); - testSubscriber.await().assertComplete().assertNoErrors().assertValueCount(5).assertValueSequence(expected); - } - - @Test(timeout = 2000) - public void testJustWithDelayedDemand() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(0); - Px.just("Hello").subscribe(subscriber); - subscriber.assertValueCount(0) - .assertNoErrors() - .assertNotComplete(); - - subscriber.request(1); - - subscriber.assertValueCount(1) - .assertValues("Hello") - .assertNoErrors() - .assertComplete(); - } - - @Test(timeout = 2000) - public void testDefer() throws Exception { - final AtomicInteger factoryInvoked = new AtomicInteger(); - TestSubscriber subscriber = TestSubscriber.create(); - Px defer = Px.defer(() -> { - factoryInvoked.incrementAndGet(); - return Px.just("Hello"); - }); - - assertThat("Publisher created before subscription.", factoryInvoked.get(), is(0)); - defer.subscribe(subscriber); - assertThat("Publisher not created on subscription.", factoryInvoked.get(), is(1)); - subscriber.assertComplete().assertNoErrors().assertValues("Hello"); - } -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/TestSchedulerTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/TestSchedulerTest.java deleted file mode 100644 index 1f4f37f19..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/TestSchedulerTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions; - -import io.reactivesocket.reactivestreams.extensions.TestScheduler; -import org.junit.Test; -import org.reactivestreams.Publisher; -import io.reactivex.subscribers.TestSubscriber; - -import java.util.concurrent.TimeUnit; - -public class TestSchedulerTest { - - @Test - public void testTimer() throws Exception { - TestScheduler scheduler = new TestScheduler(); - TestSubscriber timer1 = newTimer(scheduler, 1, TimeUnit.HOURS); - TestSubscriber timer2 = newTimer(scheduler, 2, TimeUnit.HOURS); - - scheduler.advanceTimeBy(1, TimeUnit.HOURS); - timer1.assertComplete().assertNoErrors().assertNoValues(); - timer2.assertNotTerminated(); - - scheduler.advanceTimeBy(1, TimeUnit.HOURS); - timer2.assertComplete().assertNoErrors().assertNoValues(); - } - - private static TestSubscriber newTimer(TestScheduler scheduler, int time, TimeUnit unit) { - Publisher timer = scheduler.timer(time, unit); - TestSubscriber subscriber = TestSubscriber.create(); - timer.subscribe(subscriber); - return subscriber; - } -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/EmptySubjectTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/EmptySubjectTest.java deleted file mode 100644 index 3d3e3d242..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/EmptySubjectTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal; - -import io.reactivex.subscribers.TestSubscriber; -import org.junit.Test; - -public class EmptySubjectTest { - - @Test(timeout = 10000) - public void testOnComplete() throws Exception { - EmptySubject subject = new EmptySubject(); - TestSubscriber subscriber = TestSubscriber.create(); - subject.subscribe(subscriber); - - subscriber.assertNotTerminated(); - subject.onComplete(); - - subscriber.assertComplete(); - subscriber.assertNoErrors(); - } - - @Test(timeout = 10000) - public void testOnError() throws Exception { - EmptySubject subject = new EmptySubject(); - TestSubscriber subscriber = TestSubscriber.create(); - subject.subscribe(subscriber); - - subscriber.assertNotTerminated(); - subject.onError(new NullPointerException()); - - subscriber.assertNotComplete(); - subscriber.assertError(NullPointerException.class); - } - - @Test(timeout = 10000) - public void testOnErrorBeforeSubscribe() throws Exception { - EmptySubject subject = new EmptySubject(); - subject.onError(new NullPointerException()); - TestSubscriber subscriber = TestSubscriber.create(); - subject.subscribe(subscriber); - subscriber.assertNotComplete(); - subscriber.assertError(NullPointerException.class); - } - - @Test(timeout = 10000) - public void testCompleteBeforeSubscribe() throws Exception { - EmptySubject subject = new EmptySubject(); - subject.onComplete(); - TestSubscriber subscriber = TestSubscriber.create(); - subject.subscribe(subscriber); - subscriber.assertComplete(); - subscriber.assertNoErrors(); - } -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/SerializedSubscriptionTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/SerializedSubscriptionTest.java deleted file mode 100644 index 3e605b46a..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/SerializedSubscriptionTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal; - -import org.junit.Test; -import org.reactivestreams.Subscription; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class SerializedSubscriptionTest { - - @Test - public void testReplace() throws Exception { - MockSubscription first = new MockSubscription(); - SerializedSubscription subscription = new SerializedSubscription(first); - subscription.request(10); - assertThat("Unexpected requested count.", first.getRequested(), is(10)); - subscription.onItemReceived(); - MockSubscription second = new MockSubscription(); - subscription.replaceSubscription(second); - assertThat("Previous subscription not cancelled.", first.isCancelled(), is(true)); - assertThat("Unexpected requested count.", second.getRequested(), is(9)); - subscription.request(10); - assertThat("Unexpected requested count.", second.getRequested(), is(19)); - } - - @Test - public void testReplacePostCancel() throws Exception { - MockSubscription first = new MockSubscription(); - SerializedSubscription subscription = new SerializedSubscription(first); - assertThat("Unexpected cancelled state.", first.isCancelled(), is(false)); - subscription.cancel(); - assertThat("Unexpected cancelled state.", first.isCancelled(), is(true)); - MockSubscription second = new MockSubscription(); - subscription.replaceSubscription(second); - assertThat("Subscription not cancelled.", second.isCancelled(), is(true)); - } - - private static class MockSubscription implements Subscription { - private int requested; - private volatile boolean cancelled; - - @Override - public synchronized void request(long n) { - requested = FlowControlHelper.incrementRequestN(requested, n); - } - - @Override - public void cancel() { - cancelled = true; - } - - public int getRequested() { - return requested; - } - - public boolean isCancelled() { - return cancelled; - } - } -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/ConcatPublisherTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/ConcatPublisherTest.java deleted file mode 100644 index c99b214cb..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/ConcatPublisherTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivex.Flowable; -import org.junit.Test; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivex.subscribers.TestSubscriber; - -public class ConcatPublisherTest { - - @Test - public void testBothSuccess() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.just(1).concatWith(Px.just(2)).subscribe(subscriber); - subscriber.assertComplete().assertNoErrors().assertValueCount(2).assertValues(1, 2); - } - - @Test - public void testFirstError() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.error(new NullPointerException("Deliberate exception")) - .concatWith(Px.just(2)).subscribe(subscriber); - subscriber.assertNotComplete().assertError(NullPointerException.class) - .assertNoValues(); - } - - @Test - public void testSecondError() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - Px.just(1).concatWith(Px.error(new NullPointerException("Deliberate exception"))) - .subscribe(subscriber); - subscriber.assertNotComplete().assertError(NullPointerException.class) - .assertValueCount(1).assertValues(1); - } - - @Test - public void testDelayedDemand() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(0); - Px.just(1).concatWith(Px.just(2)) - .subscribe(subscriber); - - subscriber.assertNotTerminated(); - subscriber.request(1); - subscriber.assertNotTerminated().assertValueCount(1).assertValues(1); - - subscriber.request(1); - subscriber.assertComplete().assertNoErrors().assertValueCount(2).assertValues(1, 2); - } - - @Test - public void testOverlappingDemand() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(0); - Flowable.just(1, 2, 3, 4).concatWith(Flowable.just(5, 6, 7, 8)) - .subscribe(subscriber); - - subscriber.assertNotTerminated(); - subscriber.request(3); - subscriber.assertNotTerminated().assertValueCount(3).assertValues(1, 2, 3); - - subscriber.request(5); - subscriber.assertComplete().assertNoErrors().assertValueCount(8).assertValues(1, 2, 3, 4, 5, 6, 7, 8); - } -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/DoOnEventPublisherTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/DoOnEventPublisherTest.java deleted file mode 100644 index bc9b61d64..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/DoOnEventPublisherTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivex.Flowable; -import org.junit.Test; -import org.reactivestreams.Subscription; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivex.subscribers.TestSubscriber; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class DoOnEventPublisherTest { - - @Test(timeout = 2000) - public void testDoOnSubscribe() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(); - final AtomicReference sub = new AtomicReference<>(); - Px.from(Flowable.range(1, 5)) - .doOnSubscribe(subscription -> { - sub.set(subscription); - }) - .subscribe(testSubscriber); - - testSubscriber.await().assertComplete().assertNoErrors(); - assertThat("DoOnSubscriber not called.", sub.get(), is(notNullValue())); - } - - @Test(timeout = 2000) - public void testDoOnRequest() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(1); - final AtomicLong requested = new AtomicLong(); - Px.from(Flowable.just(1)) - .doOnRequest(requestN -> { - requested.addAndGet(requestN); - }) - .subscribe(testSubscriber); - - testSubscriber.await().assertComplete().assertNoErrors().assertValueCount(1); - assertThat("Unexpected doOnNexts", requested.get(), is(1L)); - } - - @Test(timeout = 2000) - public void testDoOnCancel() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(); - final AtomicBoolean cancelled = new AtomicBoolean(); - Px.never() - .doOnCancel(() -> { - cancelled.set(true); - }) - .subscribe(testSubscriber); - - testSubscriber.cancel(); - testSubscriber.assertNotTerminated(); - assertThat("DoOnCancel not called.", cancelled.get(), is(true)); - } - - @Test(timeout = 2000) - public void testDoOnNext() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(); - final List onNexts = new ArrayList<>(); - Px.from(Flowable.range(1, 5)) - .doOnNext(next -> { - onNexts.add(next); - }) - .subscribe(testSubscriber); - - testSubscriber.await().assertComplete().assertNoErrors().assertValueCount(5); - assertThat("Unexpected doOnNexts", onNexts, contains(1,2,3,4,5)); - } - - @Test(timeout = 2000) - public void testDoOnError() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(); - final AtomicReference err = new AtomicReference<>(); - Px.from(Flowable.error(new NullPointerException("Deliberate exception"))) - .doOnError(throwable -> { - err.set(throwable); - }) - .subscribe(testSubscriber); - - testSubscriber.await().assertNotComplete().assertError(NullPointerException.class); - assertThat("Unexpected error received", err.get(), is(instanceOf(NullPointerException.class))); - } - - @Test(timeout = 2000) - public void testDoOnComplete() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(); - final AtomicBoolean completed = new AtomicBoolean(); - Px.empty() - .doOnComplete(() -> { - completed.set(true); - }) - .subscribe(testSubscriber); - - testSubscriber.assertComplete().assertNoErrors().assertNoValues(); - assertThat("DoOnComplete not called.", completed.get(), is(true)); - } - - @Test(timeout = 2000) - public void testDoOnTerminateWithError() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(); - final AtomicBoolean terminated = new AtomicBoolean(); - Px.error(new NullPointerException("Deliberate exception")) - .doOnTerminate(() -> { - terminated.set(true); - }) - .subscribe(testSubscriber); - - testSubscriber.assertNotComplete().assertError(NullPointerException.class); - assertThat("DoOnTerminate not called.", terminated.get(), is(true)); - } - - @Test(timeout = 2000) - public void testDoOnTerminateWithCompletion() throws Exception { - TestSubscriber testSubscriber = TestSubscriber.create(); - final AtomicBoolean terminated = new AtomicBoolean(); - Px.empty() - .doOnTerminate(() -> { - terminated.set(true); - }) - .subscribe(testSubscriber); - - testSubscriber.assertComplete().assertNoErrors(); - assertThat("DoOnTerminate not called.", terminated.get(), is(true)); - } - -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SingleEmissionPublishersTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SingleEmissionPublishersTest.java deleted file mode 100644 index d171cde82..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SingleEmissionPublishersTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import org.hamcrest.MatcherAssert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivex.subscribers.TestSubscriber; - -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.atomic.AtomicReference; - -import static org.hamcrest.Matchers.*; - -@RunWith(Parameterized.class) -public class SingleEmissionPublishersTest { - - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - {Px.empty(), null, null}, - {Px.error(new NullPointerException()), null, NullPointerException.class}, - {Px.just("Hello"), "Hello", null} - }); - } - - @Parameter - public Px source; - - @Parameter(1) - public String item; - - @Parameter(2) - public Class error; - - @Test - public void testNegativeRequestN() throws Exception { - final AtomicReference error = new AtomicReference<>(); - source.subscribe(new Subscriber() { - @Override - public void onSubscribe(Subscription s) { - s.request(-1); - } - - @Override - public void onNext(String s) { - // No Op - } - - @Override - public void onError(Throwable t) { - if(error.get() == null) { - error.set(t); - } - } - - @Override - public void onComplete() { - // No Op - } - }); - - MatcherAssert.assertThat("Unexpected error.", error.get(), is(instanceOf(IllegalArgumentException.class))); - } - - @Test(timeout = 2000) - public void testHappyCase() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(); - source.subscribe(subscriber); - - subscriber.await(); - - if (error == null) { - subscriber.assertNoErrors(); - if (item != null) { - subscriber.assertValueCount(1).assertValues(item); - } else { - subscriber.assertValueCount(0); - } - } else { - subscriber.assertError(error); - } - } -} diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SwitchToPublisherTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SwitchToPublisherTest.java deleted file mode 100644 index 6fb3c5d51..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/SwitchToPublisherTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivesocket.reactivestreams.extensions.Px; -import org.junit.Test; -import io.reactivex.subscribers.TestSubscriber; - -public class SwitchToPublisherTest { - - @Test - public void testSwitch() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(0); - Px.just("Hello") - .switchTo(item -> Px.just(1).concatWith(Px.just(2))) - .subscribe(subscriber); - - subscriber.assertNotTerminated().assertNoValues(); - subscriber.request(1); - subscriber.assertNotTerminated().assertValues(1); - subscriber.request(1); - subscriber.assertComplete().assertNoErrors().assertValues(1, 2); - } - - @Test - public void testSwitchWithMoreDemand() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(10); - Px.just("Hello") - .switchTo(item -> Px.just(1).concatWith(Px.just(2))) - .subscribe(subscriber); - - subscriber.assertComplete().assertNoErrors().assertValues(1, 2); - } - - @Test - public void testSwitchWithEmptyFirst() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(10); - Px.empty() - .switchTo(item -> Px.just(1).concatWith(Px.just(2))) - .subscribe(subscriber); - - subscriber.assertComplete().assertNoErrors().assertNoValues(); - } - - @Test - public void testSwitchWithErrorFirst() throws Exception { - TestSubscriber subscriber = TestSubscriber.create(10); - Px.error(new NullPointerException("Deliberate Exception")) - .switchTo(item -> Px.just(1).concatWith(Px.just(2))) - .subscribe(subscriber); - - subscriber.assertNotComplete().assertError(NullPointerException.class).assertNoValues(); - } -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/TimeoutPublisherTest.java b/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/TimeoutPublisherTest.java deleted file mode 100644 index 218e5cbe4..000000000 --- a/reactivesocket-publishers/src/test/java/io/reactivesocket/reactivestreams/extensions/internal/publishers/TimeoutPublisherTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.reactivestreams.extensions.internal.publishers; - -import io.reactivex.Flowable; -import org.junit.Test; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.TestScheduler; -import io.reactivex.subscribers.TestSubscriber; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public class TimeoutPublisherTest { - - @Test - public void testTimeout() { - TestScheduler scheduler = new TestScheduler(); - Px source = Px.never(); - TimeoutPublisher timeoutPublisher = new TimeoutPublisher<>(source, 1, TimeUnit.DAYS, scheduler); - TestSubscriber testSubscriber = TestSubscriber.create(); - timeoutPublisher.subscribe(testSubscriber); - - testSubscriber.assertNotTerminated(); - - scheduler.advanceTimeBy(1, TimeUnit.DAYS); - - testSubscriber.assertError(TimeoutException.class); - } - - @Test - public void testEmissionBeforeTimeout() { - Flowable source = Flowable.just(1); - TestScheduler scheduler = new TestScheduler(); - TimeoutPublisher timeoutPublisher = new TimeoutPublisher<>(source, 1, TimeUnit.DAYS, scheduler); - TestSubscriber testSubscriber = TestSubscriber.create(); - timeoutPublisher.subscribe(testSubscriber); - testSubscriber.assertComplete().assertNoErrors().assertValueCount(1); - } -} \ No newline at end of file diff --git a/reactivesocket-publishers/src/test/resources/log4j.properties b/reactivesocket-publishers/src/test/resources/log4j.properties deleted file mode 100644 index 6477d125f..000000000 --- a/reactivesocket-publishers/src/test/resources/log4j.properties +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright 2016 Netflix, Inc. -#

    -# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -#

    -# http://www.apache.org/licenses/LICENSE-2.0 -#

    -# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -# - - -# -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -log4j.rootLogger=INFO, stdout - -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} %5p [%t] (%F:%L) - %m%n \ No newline at end of file diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java index 696957f33..a8b089f81 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java @@ -22,13 +22,13 @@ import io.reactivesocket.client.ReactiveSocketClient; import io.reactivesocket.client.SetupProvider; import io.reactivesocket.transport.TransportClient; -import io.reactivex.Flowable; -import io.reactivex.Single; import io.reactivex.subscribers.TestSubscriber; import org.junit.rules.ExternalResource; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.scheduler.Schedulers; import java.net.SocketAddress; import java.util.concurrent.Callable; @@ -74,14 +74,14 @@ public SocketAddress getServerAddress() { public ReactiveSocket getReactiveSocket() { if (null == reactiveSocket) { - reactiveSocket = Single.fromPublisher(reactiveSocketClient.connect()).blockingGet(); + reactiveSocket = reactiveSocketClient.connect().block(); } return reactiveSocket; } public void testRequestResponseN(int count) { TestSubscriber ts = TestSubscriber.create(); - Flowable.range(1, count) + Flux.range(1, count) .flatMap(i -> getReactiveSocket() .requestResponse(TestUtil.utf8EncodedPayload("hello", "metadata"))) @@ -101,10 +101,10 @@ public void testRequestStream() { testStream(socket -> socket.requestStream(TestUtil.utf8EncodedPayload("hello", "metadata"))); } - private void testStream(Function> invoker) { + private void testStream(Function> invoker) { TestSubscriber ts = TestSubscriber.create(); - Publisher publisher = invoker.apply(reactiveSocket); - Flowable.fromPublisher(publisher).take(10).subscribe(ts); + Flux publisher = invoker.apply(reactiveSocket); + publisher.take(10).subscribe(ts); await(ts); ts.assertTerminated(); ts.assertNoErrors(); diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java index 91cef13e6..f4de1b08e 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java @@ -20,12 +20,10 @@ import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.ReactiveSocketClient; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import io.reactivex.Single; import org.HdrHistogram.Recorder; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; -import java.util.concurrent.TimeUnit; +import java.time.Duration; public class PingClient { @@ -40,14 +38,14 @@ public PingClient(ReactiveSocketClient client) { public PingClient connect() { if (null == reactiveSocket) { - reactiveSocket = Single.fromPublisher(client.connect()).blockingGet(); + reactiveSocket = client.connect().block(); } return this; } - public Recorder startTracker(long interval, TimeUnit timeUnit) { + public Recorder startTracker(Duration interval) { final Recorder histogram = new Recorder(3600000000000L, 3); - Flowable.interval(timeUnit.toNanos(interval), TimeUnit.NANOSECONDS) + Flux.interval(interval) .doOnNext(aLong -> { System.out.println("---- PING/ PONG HISTO ----"); histogram.getIntervalHistogram() @@ -58,13 +56,13 @@ public Recorder startTracker(long interval, TimeUnit timeUnit) { return histogram; } - public Flowable startPingPong(int count, final Recorder histogram) { + public Flux startPingPong(int count, final Recorder histogram) { connect(); - return Flowable.range(1, count) + return Flux.range(1, count) .flatMap(i -> { long start = System.nanoTime(); - return Flowable.fromPublisher(reactiveSocket.requestResponse(new PayloadImpl(request))) - .doOnTerminate(() -> { + return reactiveSocket.requestResponse(new PayloadImpl(request)) + .doFinally(signalType -> { long diff = System.nanoTime() - start; histogram.recordValue(diff); }); diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingHandler.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingHandler.java index 6cfea9819..ae413481c 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingHandler.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingHandler.java @@ -24,8 +24,7 @@ import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.server.ReactiveSocketServer.SocketAcceptor; import io.reactivesocket.util.PayloadImpl; -import io.reactivex.Flowable; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; import java.util.concurrent.ThreadLocalRandom; @@ -46,8 +45,8 @@ public PingHandler(byte[] pong) { public LeaseEnforcingSocket accept(ConnectionSetupPayload setupPayload, ReactiveSocket reactiveSocket) { return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { @Override - public Publisher requestResponse(Payload payload) { - return Flowable.just(new PayloadImpl(pong)); + public Mono requestResponse(Payload payload) { + return Mono.just(new PayloadImpl(pong)); } }); } diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java index 05c4ebaa1..c0b468ade 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java @@ -18,24 +18,23 @@ import io.reactivesocket.AbstractReactiveSocket; import io.reactivesocket.Payload; -import io.reactivex.Flowable; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; public class TestReactiveSocket extends AbstractReactiveSocket { @Override - public Publisher requestResponse(Payload payload) { - return Flowable.just(TestUtil.utf8EncodedPayload("hello world", "metadata")); + public Mono requestResponse(Payload payload) { + return Mono.just(TestUtil.utf8EncodedPayload("hello world", "metadata")); } @Override - public Publisher requestStream(Payload payload) { - return Flowable.fromPublisher(requestResponse(payload)).repeat(10); + public Flux requestStream(Payload payload) { + return requestResponse(payload).repeat(10); } @Override - public Publisher fireAndForget(Payload payload) { - return Subscriber::onComplete; + public Mono fireAndForget(Payload payload) { + return Mono.empty(); } } diff --git a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/AeronDuplexConnection.java b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/AeronDuplexConnection.java index 8088135b2..4f8e96718 100644 --- a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/AeronDuplexConnection.java +++ b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/AeronDuplexConnection.java @@ -18,12 +18,12 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; import io.reactivesocket.aeron.internal.reactivestreams.AeronChannel; -import io.reactivesocket.aeron.internal.reactivestreams.ReactiveStreamsRemote; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.EmptySubject; import org.agrona.LangUtil; import org.agrona.concurrent.UnsafeBuffer; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; /** * Implementation of {@link DuplexConnection} over Aeron using an {@link io.reactivesocket.aeron.internal.reactivestreams.AeronChannel} @@ -31,24 +31,24 @@ public class AeronDuplexConnection implements DuplexConnection { private final String name; private final AeronChannel channel; - private final EmptySubject emptySubject; + private final MonoProcessor emptySubject; public AeronDuplexConnection(String name, AeronChannel channel) { this.name = name; this.channel = channel; - this.emptySubject = new EmptySubject(); + this.emptySubject = MonoProcessor.create(); } @Override - public Publisher send(Publisher frame) { - Px buffers = Px.from(frame) + public Mono send(Publisher frame) { + Flux buffers = Flux.from(frame) .map(f -> new UnsafeBuffer(f.getByteBuffer())); - return channel.send(ReactiveStreamsRemote.In.from(buffers)); + return channel.send(buffers); } @Override - public Publisher receive() { + public Flux receive() { return channel .receive() .map(b -> Frame.from(b, 0, b.capacity())) @@ -61,22 +61,20 @@ public double availability() { } @Override - public Publisher close() { - return subscriber -> { + public Mono close() { + return Mono.defer(() -> { try { channel.close(); emptySubject.onComplete(); } catch (Exception e) { emptySubject.onError(e); - LangUtil.rethrowUnchecked(e); - } finally { - emptySubject.subscribe(subscriber); } - }; + return emptySubject; + }); } @Override - public Publisher onClose() { + public Mono onClose() { return emptySubject; } diff --git a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/client/AeronTransportClient.java b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/client/AeronTransportClient.java index b8cd21b1d..8c6d2b1af 100644 --- a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/client/AeronTransportClient.java +++ b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/client/AeronTransportClient.java @@ -20,9 +20,9 @@ import io.reactivesocket.aeron.AeronDuplexConnection; import io.reactivesocket.aeron.internal.reactivestreams.AeronChannel; import io.reactivesocket.aeron.internal.reactivestreams.AeronClientChannelConnector; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.transport.TransportClient; import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; import java.util.Objects; @@ -41,10 +41,10 @@ public AeronTransportClient(AeronClientChannelConnector connector, AeronClientCh } @Override - public Publisher connect() { + public Mono connect() { Publisher channelPublisher = connector.apply(config); - return Px + return Mono .from(channelPublisher) .map(aeronChannel -> new AeronDuplexConnection("client", aeronChannel)); } diff --git a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannel.java b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannel.java index 6d24f0ddb..b4f53d072 100644 --- a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannel.java +++ b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannel.java @@ -18,9 +18,9 @@ import io.aeron.Publication; import io.aeron.Subscription; import io.reactivesocket.aeron.internal.EventLoop; -import io.reactivesocket.reactivestreams.extensions.Px; import org.agrona.DirectBuffer; -import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.Objects; @@ -55,14 +55,14 @@ public AeronChannel(String name, Publication destination, Subscription source, E * @param in * @return */ - public Publisher send(ReactiveStreamsRemote.In in) { + public Mono send(Flux in) { AeronInSubscriber inSubscriber = new AeronInSubscriber(name, destination); Objects.requireNonNull(in, "in must not be null"); - return Px.completable(onComplete -> - in - .doOnCompleteOrError(onComplete, t -> { throw new RuntimeException(t); }) - .subscribe(inSubscriber) - ); + return Mono.create(sink -> + in.doOnComplete(sink::success) + .doOnError(sink::error) + .subscribe(inSubscriber) + ); } /** @@ -71,7 +71,7 @@ public Publisher send(ReactiveStreamsRemote.In in) * * @return ReactiveStreamsRemote.Out of DirectBuffer */ - public ReactiveStreamsRemote.Out receive() { + public Flux receive() { return outPublisher; } diff --git a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronClientChannelConnector.java b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronClientChannelConnector.java index d1b23ae92..2cdc214ba 100644 --- a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronClientChannelConnector.java +++ b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronClientChannelConnector.java @@ -29,12 +29,13 @@ import io.reactivesocket.aeron.internal.reactivestreams.messages.ConnectEncoder; import io.reactivesocket.aeron.internal.reactivestreams.messages.MessageHeaderDecoder; import io.reactivesocket.aeron.internal.reactivestreams.messages.MessageHeaderEncoder; -import io.reactivesocket.reactivestreams.extensions.Px; import org.agrona.DirectBuffer; import org.agrona.concurrent.UnsafeBuffer; -import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoSource; +import reactor.core.publisher.Operators; import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentHashMap; @@ -134,9 +135,9 @@ private int poll() { } @Override - public Publisher apply(AeronClientConfig aeronClientConfig) { - return subscriber -> { - subscriber.onSubscribe(Px.EMPTY_SUBSCRIPTION); + public Mono apply(AeronClientConfig aeronClientConfig) { + return MonoSource.wrap(subscriber -> { + subscriber.onSubscribe(Operators.emptySubscription()); final long channelId = CHANNEL_ID_COUNTER.get(); try { @@ -202,7 +203,7 @@ public Publisher apply(AeronClientConfig aeronClientConfig) { clientSubscriptions.remove(channelId); subscriber.onError(t); } - }; + }); } public DirectBuffer encodeConnectMessage(long channelId, AeronClientConfig config, int clientSessionId) { diff --git a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronOutPublisher.java b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronOutPublisher.java index 781624ea1..3d9e2b6bd 100644 --- a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronOutPublisher.java +++ b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/AeronOutPublisher.java @@ -26,6 +26,7 @@ import org.reactivestreams.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import reactor.core.publisher.Flux; import java.nio.ByteBuffer; import java.util.Objects; @@ -34,7 +35,7 @@ /** * */ -public class AeronOutPublisher implements ReactiveStreamsRemote.Out { +public class AeronOutPublisher extends Flux { private static final Logger logger = LoggerFactory.getLogger(AeronOutPublisher.class); private final io.aeron.Subscription source; private final EventLoop eventLoop; diff --git a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/ReactiveStreamsRemote.java b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/ReactiveStreamsRemote.java index 781cfa94e..17b8caba8 100644 --- a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/ReactiveStreamsRemote.java +++ b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/internal/reactivestreams/ReactiveStreamsRemote.java @@ -15,8 +15,9 @@ */ package io.reactivesocket.aeron.internal.reactivestreams; -import io.reactivesocket.reactivestreams.extensions.Px; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.net.SocketAddress; import java.util.concurrent.TimeUnit; @@ -27,34 +28,14 @@ * Interfaces to define a ReactiveStream over a remote channel */ public interface ReactiveStreamsRemote { - interface In extends Px { - static In from(Publisher source) { - if (source instanceof In) { - return (In) source; - } else { - return source::subscribe; - } - } - } - - interface Out extends Px { - static Out from(Publisher source) { - if (source instanceof Out) { - return (Out) source; - } else { - return source::subscribe; - } - } - } - interface Channel { - Publisher send(ReactiveStreamsRemote.In in); + Mono send(Flux in); - default Publisher send(T t) { - return send(ReactiveStreamsRemote.In.from(Px.just(t))); + default Mono send(T t) { + return send(Flux.just(t)); } - ReactiveStreamsRemote.Out receive(); + Flux receive(); boolean isActive(); } diff --git a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/server/AeronTransportServer.java b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/server/AeronTransportServer.java index 7c679cec1..bec5e593c 100644 --- a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/server/AeronTransportServer.java +++ b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/server/AeronTransportServer.java @@ -23,7 +23,6 @@ import io.reactivesocket.aeron.internal.reactivestreams.AeronChannelServer; import io.reactivesocket.aeron.internal.reactivestreams.AeronSocketAddress; import io.reactivesocket.aeron.internal.reactivestreams.ReactiveStreamsRemote; -import io.reactivesocket.reactivestreams.extensions.Px; import io.reactivesocket.transport.TransportServer; import java.net.SocketAddress; @@ -55,7 +54,7 @@ public StartedServer start(ConnectionAcceptor acceptor) { aeronChannelServer = AeronChannelServer.create( aeronChannel -> { DuplexConnection connection = new AeronDuplexConnection("server", aeronChannel); - Px.from(acceptor.apply(connection)).subscribe(); + acceptor.apply(connection).subscribe(); }, aeronWrapper, managementSubscriptionSocket, diff --git a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/AeronPing.java b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/AeronPing.java index d278b24f1..5534c976b 100644 --- a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/AeronPing.java +++ b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/AeronPing.java @@ -30,7 +30,6 @@ import org.HdrHistogram.Recorder; import java.time.Duration; -import java.util.concurrent.TimeUnit; public final class AeronPing { @@ -64,14 +63,14 @@ public static void main(String... args) throws Exception { ReactiveSocketClient client = ReactiveSocketClient.create(aeronTransportClient, setup); PingClient pingClient = new PingClient(client); - Recorder recorder = pingClient.startTracker(1, TimeUnit.SECONDS); + Recorder recorder = pingClient.startTracker(Duration.ofSeconds(1)); final int count = 1_000_000_000; pingClient.connect() .startPingPong(count, recorder) .doOnTerminate(() -> { System.out.println("Sent " + count + " messages."); }) - .last(null).blockingGet(); + .last(null).block(); System.exit(0); } diff --git a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelPing.java b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelPing.java index 8131674f6..5f196df9d 100644 --- a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelPing.java +++ b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelPing.java @@ -19,11 +19,9 @@ import io.reactivesocket.aeron.internal.AeronWrapper; import io.reactivesocket.aeron.internal.DefaultAeronWrapper; import io.reactivesocket.aeron.internal.SingleThreadedEventLoop; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivex.Flowable; -import io.reactivex.Single; import org.HdrHistogram.Recorder; import org.agrona.concurrent.UnsafeBuffer; +import reactor.core.publisher.Flux; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -57,11 +55,11 @@ public static void main(String... args) throws Exception { config = AeronClientChannelConnector.AeronClientConfig.create(receiveAddress ,sendAddress, 1, 2, eventLoop); - AeronChannel channel = Single.fromPublisher(connector.apply(config)).blockingGet(); + AeronChannel channel = connector.apply(config).block(); AtomicLong lastUpdate = new AtomicLong(System.nanoTime()); - Px.from(channel - .receive()) + channel + .receive() .doOnNext(b -> { synchronized (wrapper) { int anInt = b.getInt(0); @@ -76,14 +74,14 @@ public static void main(String... args) throws Exception { .subscribe(); byte[] b = new byte[1024]; - Flowable.range(0, count) + Flux.range(0, count) .flatMap(i -> { UnsafeBuffer buffer = new UnsafeBuffer(b); buffer.putInt(0, i); - return channel.send(ReactiveStreamsRemote.In.from(Px.just(buffer))); + return channel.send(buffer); }, 8) .last(null) - .blockingGet(); + .block(); } } diff --git a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelPongServer.java b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelPongServer.java index a98c62d3c..fe4734e46 100644 --- a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelPongServer.java +++ b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelPongServer.java @@ -20,8 +20,8 @@ import io.reactivesocket.aeron.internal.AeronWrapper; import io.reactivesocket.aeron.internal.DefaultAeronWrapper; import io.reactivesocket.aeron.internal.SingleThreadedEventLoop; -import io.reactivesocket.reactivestreams.extensions.Px; import org.agrona.DirectBuffer; +import reactor.core.publisher.Flux; /** * @@ -36,13 +36,12 @@ public static void main(String... args) { AeronChannelServer.AeronChannelConsumer consumer = new AeronChannelServer.AeronChannelConsumer() { @Override public void accept(AeronChannel aeronChannel) { - Px receive = + Flux receive = aeronChannel .receive(); //.doOnNext(b -> System.out.println("server got => " + b.getInt(0))); - Px - .from(aeronChannel.send(ReactiveStreamsRemote.In.from(receive))) + aeronChannel.send(receive) .doOnError(throwable -> throwable.printStackTrace()) .subscribe(); } diff --git a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelTest.java b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelTest.java index 1ba2b5f1a..6f4718368 100644 --- a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelTest.java +++ b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronChannelTest.java @@ -22,12 +22,12 @@ import io.reactivesocket.aeron.internal.Constants; import io.reactivesocket.aeron.internal.EventLoop; import io.reactivesocket.aeron.internal.SingleThreadedEventLoop; -import io.reactivex.Flowable; import org.agrona.BitUtil; import org.agrona.LangUtil; import org.agrona.concurrent.UnsafeBuffer; import org.junit.Ignore; import org.junit.Test; +import reactor.core.publisher.Flux; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; @@ -112,14 +112,14 @@ public void testPing() { EventLoop serverLoop = new SingleThreadedEventLoop("server"); AeronOutPublisher publisher = new AeronOutPublisher("server", clientPublication.sessionId(), serverSubscription, serverLoop); - Flowable.fromPublisher(publisher) + publisher .doOnNext(i -> countDownLatch.countDown()) .doOnError(Throwable::printStackTrace) .subscribe(); AeronInSubscriber aeronInSubscriber = new AeronInSubscriber("client", clientPublication); - Flowable unsafeBufferObservable = Flowable + Flux unsafeBufferObservable = Flux .range(1, count) //.doOnNext(i -> LockSupport.parkNanos(TimeUnit.MICROSECONDS.toNanos(50))) // .doOnNext(i -> System.out.println(Thread.currentThread() + " => client sending => " + i)) @@ -261,7 +261,7 @@ private void pingPong(int count) { CountDownLatch latch = new CountDownLatch(count); - Flowable.fromPublisher(serverChannel.receive()) + serverChannel.receive() .flatMap(f -> { // latch.countDown(); //System.out.println("received -> " + f.getInt(0)); @@ -291,7 +291,7 @@ private void pingPong(int count) { byte[] bytes = new byte[8]; ThreadLocalRandom.current().nextBytes(bytes); - Flowable + Flux .range(1, count) //.doOnRequest(l -> System.out.println("requested => " + l)) .flatMap(i -> { diff --git a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronClientServerChannelTest.java b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronClientServerChannelTest.java index 292d896f3..d3e8868a8 100644 --- a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronClientServerChannelTest.java +++ b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/internal/reactivestreams/AeronClientServerChannelTest.java @@ -21,9 +21,6 @@ import io.reactivesocket.aeron.internal.DefaultAeronWrapper; import io.reactivesocket.aeron.internal.EventLoop; import io.reactivesocket.aeron.internal.SingleThreadedEventLoop; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivex.Flowable; -import io.reactivex.Single; import org.agrona.BitUtil; import org.agrona.DirectBuffer; import org.agrona.concurrent.UnsafeBuffer; @@ -31,6 +28,8 @@ import org.junit.Ignore; import org.junit.Test; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; @@ -88,7 +87,7 @@ public void testConnect() throws Exception { Publisher publisher = connector .apply(config); - Px + Flux .from(publisher) .doOnNext(Assert::assertNotNull) .doOnNext(c -> latch.countDown()) @@ -131,14 +130,13 @@ public void testPingPong() throws Exception { AeronChannelServer.AeronChannelConsumer consumer = (AeronChannel aeronChannel) -> { Assert.assertNotNull(aeronChannel); - ReactiveStreamsRemote.Out receive = aeronChannel + Flux receive = aeronChannel .receive(); - Flowable data = Flowable.fromPublisher(receive) + Flux data = receive .doOnNext(b -> System.out.println("server received => " + b.getInt(0))); - Flowable - .fromPublisher(aeronChannel.send(ReactiveStreamsRemote.In.from(data))) + aeronChannel.send(data) .subscribe(); }; @@ -155,10 +153,10 @@ public void testPingPong() throws Exception { int count = 10; CountDownLatch latch = new CountDownLatch(count); - Single.fromPublisher(publisher) - .flatMap(aeronChannel -> - Single.create(callback -> { - Flowable data = Flowable + Mono.from(publisher) + .then(aeronChannel -> + Mono.create(callback -> { + Flux data = Flux .range(1, count) .map(i -> { byte[] b = new byte[BitUtil.SIZE_OF_INT]; @@ -167,9 +165,9 @@ public void testPingPong() throws Exception { return buffer; }); - Flowable.fromPublisher(aeronChannel.receive()).doOnNext(b -> latch.countDown()) - .doOnNext(callback::onSuccess).subscribe(); - Flowable.fromPublisher(aeronChannel.send(ReactiveStreamsRemote.In.from(data))) + aeronChannel.receive().doOnNext(b -> latch.countDown()) + .doOnNext(callback::success).subscribe(); + aeronChannel.send(data) .subscribe(); }) ) diff --git a/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/LocalClient.java b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/LocalClient.java index 04ac11946..9aa0aede9 100644 --- a/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/LocalClient.java +++ b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/LocalClient.java @@ -19,8 +19,7 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.local.internal.PeerConnector; import io.reactivesocket.transport.TransportClient; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscription; +import reactor.core.publisher.Mono; import java.util.concurrent.atomic.AtomicInteger; @@ -39,41 +38,12 @@ private LocalClient(LocalServer peer) { } @Override - public Publisher connect() { - return sub -> { - sub.onSubscribe(new Subscription() { - private boolean emit = true; - - @Override - public void request(long n) { - synchronized (this) { - if (!emit) { - return; - } - emit = false; - } - - if (n < 0) { - sub.onError(new IllegalArgumentException("Rule 3.9: n > 0 is required, but it was " + n)); - } else { - PeerConnector peerConnector = PeerConnector.connect(peer.getName(), - connIdGenerator.incrementAndGet()); - try { - peer.accept(peerConnector); - sub.onNext(peerConnector.forClient()); - sub.onComplete(); - } catch (Exception e) { - sub.onError(e); - } - } - } - - @Override - public synchronized void cancel() { - emit = false; - } - }); - }; + public Mono connect() { + return Mono.fromCallable(() -> { + PeerConnector peerConnector = PeerConnector.connect(peer.getName(), connIdGenerator.incrementAndGet()); + peer.accept(peerConnector); + return peerConnector.forClient(); + }); } /** diff --git a/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/LocalServer.java b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/LocalServer.java index 3e0f7b916..c5e3edc5d 100644 --- a/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/LocalServer.java +++ b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/LocalServer.java @@ -18,9 +18,6 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.local.internal.PeerConnector; -import io.reactivesocket.reactivestreams.extensions.DefaultSubscriber; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; import io.reactivesocket.transport.TransportServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,11 +80,12 @@ void accept(PeerConnector peerConnector) { DuplexConnection serverConn = peerConnector.forServer(); activeConnections.add(serverConn); - serverConn.onClose().subscribe(Subscribers.doOnTerminate(() -> activeConnections.remove(serverConn))); - Px.from(started.acceptor.apply(serverConn)) - .subscribe(Subscribers.cleanup(() -> { - serverConn.close().subscribe(Subscribers.empty()); - })); + serverConn.onClose() + .doFinally(signalType -> activeConnections.remove(serverConn)) + .subscribe(); + started.acceptor.apply(serverConn) + .doFinally(signalType -> serverConn.close().subscribe()) + .subscribe(); } boolean isActive() { @@ -149,7 +147,7 @@ public void awaitShutdown(long duration, TimeUnit durationUnit) { public void shutdown() { shutdownLatch.countDown(); for (DuplexConnection activeConnection : activeConnections) { - activeConnection.close().subscribe(DefaultSubscriber.defaultInstance()); + activeConnection.close().subscribe(); } LocalPeersManager.unregister(name); } diff --git a/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/PeerConnector.java b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/PeerConnector.java index 585dc9408..12b5f0213 100644 --- a/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/PeerConnector.java +++ b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/PeerConnector.java @@ -18,17 +18,15 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; -import io.reactivesocket.reactivestreams.extensions.Px; -import io.reactivesocket.reactivestreams.extensions.internal.EmptySubject; -import io.reactivesocket.reactivestreams.extensions.internal.ValidatingSubscription; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.CancellableSubscriber; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; +import io.reactivesocket.internal.ValidatingSubscription; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; import java.nio.channels.ClosedChannelException; -import java.util.function.Consumer; public class PeerConnector { @@ -37,11 +35,11 @@ public class PeerConnector { private final LocalDuplexConnection client; private final LocalDuplexConnection server; private final String name; - private final EmptySubject closeNotifier; + private final MonoProcessor closeNotifier; private PeerConnector(String name) { this.name = name; - closeNotifier = new EmptySubject(); + closeNotifier = MonoProcessor.create(); server = new LocalDuplexConnection(closeNotifier, false); client = new LocalDuplexConnection(closeNotifier, true); server.connect(client); @@ -69,41 +67,36 @@ private final class LocalDuplexConnection implements DuplexConnection { private volatile ValidatingSubscription receiver; private volatile boolean connected; - private final EmptySubject closeNotifier; + private final MonoProcessor closeNotifier; private final boolean client; private volatile LocalDuplexConnection peer; - private LocalDuplexConnection(EmptySubject closeNotifier, boolean client) { + private LocalDuplexConnection(MonoProcessor closeNotifier, boolean client) { this.closeNotifier = closeNotifier; this.client = client; - closeNotifier.subscribe(Subscribers.doOnTerminate(() -> { - connected = false; - if (receiver != null) { - receiver.safeOnError(new ClosedChannelException()); - } - })); + closeNotifier + .doFinally(signalType -> { + connected = false; + if (receiver != null) { + receiver.safeOnError(new ClosedChannelException()); + } + }).subscribe(); } @Override - public Publisher send(Publisher frames) { - return s -> { - CancellableSubscriber writeSub = Subscribers.create(subscription -> { - subscription.request(Long.MAX_VALUE); // Local transport is not flow controlled. - }, frame -> { - if (peer != null) { - peer.receiveFrameFromPeer(frame); - } else { - logger.warn("Sending a frame but peer not connected. Ignoring frame: " + frame); - } - }, s::onError, s::onComplete, null); - s.onSubscribe(ValidatingSubscription.onCancel(s, () -> writeSub.cancel())); - frames.subscribe(writeSub); - }; + public Mono send(Publisher frames) { + return Flux.from(frames).doOnNext(frame -> { + if (peer != null) { + peer.receiveFrameFromPeer(frame); + } else { + logger.warn("Sending a frame but peer not connected. Ignoring frame: " + frame); + } + }).then(); } @Override - public Publisher receive() { - return sub -> { + public Flux receive() { + return Flux.from(sub -> { boolean invalid = false; synchronized (this) { if (receiver != null && receiver.isActive()) { @@ -119,7 +112,7 @@ public Publisher receive() { } else { sub.onSubscribe(receiver); } - }; + }); } @Override @@ -128,15 +121,15 @@ public double availability() { } @Override - public Publisher close() { - return Px.defer(() -> { + public Mono close() { + return Mono.defer(() -> { closeNotifier.onComplete(); return closeNotifier; }); } @Override - public Publisher onClose() { + public Mono onClose() { return closeNotifier; } diff --git a/reactivesocket-transport-local/src/test/java/io/reactivesocket/GracefulShutdownTest.java b/reactivesocket-transport-local/src/test/java/io/reactivesocket/GracefulShutdownTest.java index 613a3c672..9a0072ff4 100644 --- a/reactivesocket-transport-local/src/test/java/io/reactivesocket/GracefulShutdownTest.java +++ b/reactivesocket-transport-local/src/test/java/io/reactivesocket/GracefulShutdownTest.java @@ -15,12 +15,14 @@ import io.reactivesocket.test.util.LocalRSRule; import io.reactivex.subscribers.TestSubscriber; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; +@Ignore public class GracefulShutdownTest { @Rule public final LocalRSRule rule = new LocalRSRule(); diff --git a/reactivesocket-transport-local/src/test/java/io/reactivesocket/test/util/LocalRSRule.java b/reactivesocket-transport-local/src/test/java/io/reactivesocket/test/util/LocalRSRule.java index 60680d612..ce2fe9cc7 100644 --- a/reactivesocket-transport-local/src/test/java/io/reactivesocket/test/util/LocalRSRule.java +++ b/reactivesocket-transport-local/src/test/java/io/reactivesocket/test/util/LocalRSRule.java @@ -23,11 +23,8 @@ import io.reactivesocket.lease.Lease; import io.reactivesocket.lease.LeaseImpl; import io.reactivesocket.local.LocalSendReceiveTest.LocalRule; -import io.reactivesocket.reactivestreams.extensions.internal.CancellableImpl; -import io.reactivesocket.reactivestreams.extensions.internal.subscribers.Subscribers; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.transport.TransportServer.StartedServer; -import io.reactivex.Single; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.mockito.ArgumentCaptor; @@ -58,13 +55,13 @@ public void evaluate() throws Throwable { leases = new CopyOnWriteArrayList<>(); acceptingSocketCloses = new CopyOnWriteArrayList<>(); leaseDistributorMock = Mockito.mock(LeaseDistributor.class); - Mockito.when(leaseDistributorMock.registerSocket(any())).thenReturn(new CancellableImpl()); + Mockito.when(leaseDistributorMock.registerSocket(any())).thenReturn(() -> {}); init(); socketServer = ReactiveSocketServer.create(localServer); started = socketServer.start((setup, sendingSocket) -> { AbstractReactiveSocket accept = new AbstractReactiveSocket() { }; - accept.onClose().subscribe(Subscribers.doOnTerminate(() -> acceptingSocketCloses.add(true))); + accept.onClose().doFinally(signalType -> acceptingSocketCloses.add(true)).subscribe(); return new DefaultLeaseEnforcingSocket(accept, leaseDistributorMock); }); socketClient = ReactiveSocketClient.create(localClient, keepAlive(never()) @@ -80,7 +77,7 @@ public void accept(Lease lease) { } public ReactiveSocket connectSocket() { - return Single.fromPublisher(socketClient.connect()).blockingGet(); + return socketClient.connect().block(); } @SuppressWarnings({"rawtypes", "unchecked"}) diff --git a/reactivesocket-transport-tcp/build.gradle b/reactivesocket-transport-netty/build.gradle similarity index 86% rename from reactivesocket-transport-tcp/build.gradle rename to reactivesocket-transport-netty/build.gradle index 6b094cf01..f73a70e0d 100644 --- a/reactivesocket-transport-tcp/build.gradle +++ b/reactivesocket-transport-netty/build.gradle @@ -16,8 +16,7 @@ dependencies { compile project(':reactivesocket-core') - compile 'io.reactivex:rxnetty-tcp:0.5.2-rc.5' - compile 'io.reactivex:rxjava-reactive-streams:1.2.0' + compile 'io.projectreactor.ipc:reactor-netty:0.6.1.RELEASE' testCompile project(':reactivesocket-test') } diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java new file mode 100644 index 000000000..2a7abc3f9 --- /dev/null +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.transport.netty; + +import io.netty.buffer.ByteBuf; +import io.reactivesocket.DuplexConnection; +import io.reactivesocket.Frame; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.NettyContext; +import reactor.ipc.netty.NettyInbound; +import reactor.ipc.netty.NettyOutbound; + +import java.nio.ByteBuffer; + +public class NettyDuplexConnection implements DuplexConnection { + private final NettyInbound in; + private final NettyOutbound out; + private final NettyContext context; + + public NettyDuplexConnection(NettyInbound in, NettyOutbound out, NettyContext context) { + this.in = in; + this.out = out; + this.context = context; + //context.onClose(() -> close().subscribe()); + } + + @Override + public Mono send(Publisher frames) { + return Flux.from(frames) + .concatMap(this::sendOne) + .then(); + } + + @Override + public Mono sendOne(Frame frame) { + ByteBuffer src = frame.getByteBuffer(); + ByteBuf msg = out.alloc().buffer(src.remaining()).writeBytes(src); + return out.sendObject(msg).then(); + } + + @Override + public Flux receive() { + return in + .receive() + .map(byteBuf -> { + ByteBuffer buffer = ByteBuffer.allocate(byteBuf.capacity()); + byteBuf.getBytes(0, buffer); + return Frame.from(buffer); + }); + } + + @Override + public Mono close() { + return Mono.fromRunnable(() -> { + if (!context.isDisposed()) { + context.channel().close(); + } + }); + } + + @Override + public Mono onClose() { + return context.onClose(); + } + + @Override + public double availability() { + return context.isDisposed() ? 0.0 : 1.0; + } +} \ No newline at end of file diff --git a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketLengthCodec.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/ReactiveSocketLengthCodec.java similarity index 95% rename from reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketLengthCodec.java rename to reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/ReactiveSocketLengthCodec.java index 9af79df56..ea02933b7 100644 --- a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketLengthCodec.java +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/ReactiveSocketLengthCodec.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.reactivesocket.transport.tcp; +package io.reactivesocket.transport.netty; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/client/TcpTransportClient.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/client/TcpTransportClient.java new file mode 100644 index 000000000..6916c8357 --- /dev/null +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/client/TcpTransportClient.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivesocket.transport.netty.client; + +import io.reactivesocket.DuplexConnection; +import io.reactivesocket.transport.TransportClient; +import io.reactivesocket.transport.netty.ReactiveSocketLengthCodec; +import io.reactivesocket.transport.netty.NettyDuplexConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.tcp.TcpClient; + +public class TcpTransportClient implements TransportClient { + private final Logger logger = LoggerFactory.getLogger(TcpTransportClient.class); + private final TcpClient client; + + private TcpTransportClient(TcpClient client) { + this.client = client; + } + + public static TcpTransportClient create(TcpClient client) { + return new TcpTransportClient(client); + } + + @Override + public Mono connect() { + return Mono.create(sink -> + client.newHandler((in, out) -> { + in.context().addHandler("client-length-codec", new ReactiveSocketLengthCodec()); + NettyDuplexConnection connection = new NettyDuplexConnection(in, out, in.context()); + sink.success(connection); + return connection.onClose(); + }) + .doOnError(sink::error) + .subscribe() + ); + } +} diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/server/TcpTransportServer.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/server/TcpTransportServer.java new file mode 100644 index 000000000..68df2662c --- /dev/null +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/server/TcpTransportServer.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivesocket.transport.netty.server; + +import io.reactivesocket.transport.TransportServer; +import io.reactivesocket.transport.netty.ReactiveSocketLengthCodec; +import io.reactivesocket.transport.netty.NettyDuplexConnection; +import reactor.ipc.netty.NettyContext; +import reactor.ipc.netty.tcp.TcpServer; + +import java.net.SocketAddress; +import java.util.concurrent.TimeUnit; + +public class TcpTransportServer implements TransportServer { + TcpServer server; + + public TcpTransportServer(TcpServer server) { + this.server = server; + } + + public static TcpTransportServer create(TcpServer server) { + return new TcpTransportServer(server); + } + + @Override + public StartedServer start(ConnectionAcceptor acceptor) { + NettyContext context = server.newHandler((in, out) -> { + in.context().addHandler("server-length-codec", new ReactiveSocketLengthCodec()); + NettyDuplexConnection connection = new NettyDuplexConnection(in, out, in.context()); + acceptor.apply(connection).subscribe(); + + return out.neverComplete(); + }).block(); + + return new StartServerImpl(context); + } + + static class StartServerImpl implements StartedServer { + NettyContext context; + + StartServerImpl(NettyContext context) { + this.context = context; + } + + @Override + public SocketAddress getServerAddress() { + return context.address(); + } + + @Override + public int getServerPort() { + return context.address().getPort(); + } + + @Override + public void awaitShutdown() { + context.onClose().block(); + } + + @Override + public void awaitShutdown(long duration, TimeUnit durationUnit) { + context.onClose().blockMillis(TimeUnit.MILLISECONDS.convert(duration, durationUnit)); + } + + @Override + public void shutdown() { + context.dispose(); + } + } + +} \ No newline at end of file diff --git a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/ClientServerTest.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/ClientServerTest.java similarity index 97% rename from reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/ClientServerTest.java rename to reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/ClientServerTest.java index 3fd26e46a..7ff0805d0 100644 --- a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/ClientServerTest.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/ClientServerTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.reactivesocket.transport.tcp; +package io.reactivesocket.transport.netty; import io.reactivesocket.test.ClientSetupRule; import org.junit.Ignore; diff --git a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpClientSetupRule.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientSetupRule.java similarity index 72% rename from reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpClientSetupRule.java rename to reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientSetupRule.java index f3f8bb24e..a2634e011 100644 --- a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpClientSetupRule.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientSetupRule.java @@ -14,23 +14,24 @@ * limitations under the License. */ -package io.reactivesocket.transport.tcp; +package io.reactivesocket.transport.netty; import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.test.ClientSetupRule; import io.reactivesocket.test.TestReactiveSocket; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.transport.netty.server.TcpTransportServer; +import reactor.ipc.netty.tcp.TcpClient; +import reactor.ipc.netty.tcp.TcpServer; -import static io.netty.handler.logging.LogLevel.DEBUG; +import java.net.InetSocketAddress; public class TcpClientSetupRule extends ClientSetupRule { public TcpClientSetupRule() { - super(TcpTransportClient::create, () -> { - return ReactiveSocketServer.create(TcpTransportServer.create(0) - .configureServer(server -> server.enableWireLogging("test-server", DEBUG))) + super(address -> TcpTransportClient.create(TcpClient.create(options -> options.connect((InetSocketAddress) address))), () -> { + return ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) .start((setup, sendingSocket) -> { return new DisabledLeaseAcceptingSocket(new TestReactiveSocket()); }) diff --git a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpPing.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPing.java similarity index 80% rename from reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpPing.java rename to reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPing.java index c0d11b19c..429005841 100644 --- a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpPing.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPing.java @@ -13,34 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.reactivesocket.transport.tcp; +package io.reactivesocket.transport.netty; import io.reactivesocket.client.KeepAliveProvider; import io.reactivesocket.client.ReactiveSocketClient; import io.reactivesocket.client.SetupProvider; import io.reactivesocket.test.PingClient; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; +import io.reactivesocket.transport.netty.client.TcpTransportClient; import org.HdrHistogram.Recorder; +import reactor.ipc.netty.tcp.TcpClient; import java.net.InetSocketAddress; import java.time.Duration; -import java.util.concurrent.TimeUnit; public final class TcpPing { public static void main(String... args) throws Exception { SetupProvider setup = SetupProvider.keepAlive(KeepAliveProvider.never()).disableLease(); ReactiveSocketClient client = - ReactiveSocketClient.create(TcpTransportClient.create(new InetSocketAddress("localhost", 7878)), setup); + ReactiveSocketClient.create(TcpTransportClient.create(TcpClient.create(options -> options.connect(new InetSocketAddress("localhost", 7878)))), setup); PingClient pingClient = new PingClient(client); - Recorder recorder = pingClient.startTracker(1, TimeUnit.SECONDS); + Recorder recorder = pingClient.startTracker(Duration.ofSeconds(1)); final int count = 1_000_000_000; pingClient.connect() .startPingPong(count, recorder) .doOnTerminate(() -> { System.out.println("Sent " + count + " messages."); }) - .last(null) - .blockingGet(); + .blockLast(); } } diff --git a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpPongServer.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPongServer.java similarity index 79% rename from reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpPongServer.java rename to reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPongServer.java index 5e5685a45..9ee026351 100644 --- a/reactivesocket-transport-tcp/src/test/java/io/reactivesocket/transport/tcp/TcpPongServer.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPongServer.java @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.reactivesocket.transport.tcp; +package io.reactivesocket.transport.netty; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.test.PingHandler; -import io.reactivesocket.transport.tcp.server.TcpTransportServer; +import io.reactivesocket.transport.netty.server.TcpTransportServer; +import reactor.ipc.netty.tcp.TcpServer; public final class TcpPongServer { public static void main(String... args) throws Exception { - ReactiveSocketServer.create(TcpTransportServer.create(7878)) + ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create(7878))) .start(new PingHandler()) .awaitShutdown(); } diff --git a/reactivesocket-transport-tcp/src/test/resources/log4j.properties b/reactivesocket-transport-netty/src/test/resources/log4j.properties similarity index 100% rename from reactivesocket-transport-tcp/src/test/resources/log4j.properties rename to reactivesocket-transport-netty/src/test/resources/log4j.properties diff --git a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/MutableDirectByteBuf.java b/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/MutableDirectByteBuf.java deleted file mode 100644 index 89a4bc782..000000000 --- a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/MutableDirectByteBuf.java +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.transport.tcp; - -import io.netty.buffer.ByteBuf; -import org.agrona.BitUtil; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; - -public class MutableDirectByteBuf implements MutableDirectBuffer -{ - private ByteBuf byteBuf; - - public MutableDirectByteBuf(final ByteBuf byteBuf) - { - this.byteBuf = byteBuf; - } - - public void wrap(final ByteBuf byteBuf) - { - this.byteBuf = byteBuf; - } - - public ByteBuf byteBuf() - { - return byteBuf; - } - - // TODO: make utility in reactivesocket-java - public static ByteBuffer slice(final ByteBuffer byteBuffer, final int position, final int limit) - { - final int savedPosition = byteBuffer.position(); - final int savedLimit = byteBuffer.limit(); - - byteBuffer.limit(limit).position(position); - - final ByteBuffer result = byteBuffer.slice(); - - byteBuffer.limit(savedLimit).position(savedPosition); - return result; - } - - @Override - public boolean isExpandable() { - return false; - } - - @Override - public void setMemory(int index, int length, byte value) - { - for (int i = index; i < (index + length); i++) - { - byteBuf.setByte(i, value); - } - } - - @Override - public void putLong(int index, long value, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - byteBuf.setLong(index, value); - } - - @Override - public void putLong(int index, long value) - { - byteBuf.setLong(index, value); - } - - @Override - public void putInt(int index, int value, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - byteBuf.setInt(index, value); - } - - @Override - public void putInt(int index, int value) - { - byteBuf.setInt(index, value); - } - - @Override - public void putDouble(int index, double value, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - byteBuf.setDouble(index, value); - } - - @Override - public void putDouble(int index, double value) - { - byteBuf.setDouble(index, value); - } - - @Override - public void putFloat(int index, float value, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - byteBuf.setFloat(index, value); - } - - @Override - public void putFloat(int index, float value) - { - byteBuf.setFloat(index, value); - } - - @Override - public void putShort(int index, short value, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - byteBuf.setShort(index, value); - } - - @Override - public void putShort(int index, short value) - { - byteBuf.setShort(index, value); - } - - @Override - public void putByte(int index, byte value) - { - byteBuf.setByte(index, value); - } - - @Override - public void putBytes(int index, byte[] src) - { - byteBuf.setBytes(index, src); - } - - @Override - public void putBytes(int index, byte[] src, int offset, int length) - { - byteBuf.setBytes(index, src, offset, length); - } - - @Override - public void putBytes(int index, ByteBuffer srcBuffer, int length) - { - final ByteBuffer sliceBuffer = slice(srcBuffer, 0, length); - byteBuf.setBytes(index, sliceBuffer); - } - - @Override - public void putBytes(int index, ByteBuffer srcBuffer, int srcIndex, int length) - { - final ByteBuffer sliceBuffer = slice(srcBuffer, srcIndex, srcIndex + length); - byteBuf.setBytes(index, sliceBuffer); - } - - @Override - public void putBytes(int index, DirectBuffer srcBuffer, int srcIndex, int length) - { - throw new UnsupportedOperationException("putBytes(DirectBuffer) not supported"); - } - - @Override - public int putStringUtf8(int offset, String value, ByteOrder byteOrder) - { - throw new UnsupportedOperationException("putStringUtf8 not supported"); - } - - @Override - public int putStringUtf8(int offset, String value, ByteOrder byteOrder, int maxEncodedSize) - { - throw new UnsupportedOperationException("putStringUtf8 not supported"); - } - - @Override - public int putStringWithoutLengthUtf8(int offset, String value) - { - throw new UnsupportedOperationException("putStringUtf8 not supported"); - } - - @Override - public void wrap(byte[] buffer) - { - throw new UnsupportedOperationException("wrap(byte[]) not supported"); - } - - @Override - public void wrap(byte[] buffer, int offset, int length) - { - throw new UnsupportedOperationException("wrap(byte[]) not supported"); - } - - @Override - public void wrap(ByteBuffer buffer) - { - throw new UnsupportedOperationException("wrap(ByteBuffer) not supported"); - } - - @Override - public void wrap(ByteBuffer buffer, int offset, int length) - { - throw new UnsupportedOperationException("wrap(ByteBuffer) not supported"); - } - - @Override - public void wrap(DirectBuffer buffer) - { - throw new UnsupportedOperationException("wrap(DirectBuffer) not supported"); - } - - @Override - public void wrap(DirectBuffer buffer, int offset, int length) - { - throw new UnsupportedOperationException("wrap(DirectBuffer) not supported"); - } - - @Override - public void wrap(long address, int length) - { - throw new UnsupportedOperationException("wrap(address) not supported"); - } - - @Override - public long addressOffset() - { - return byteBuf.memoryAddress(); - } - - @Override - public byte[] byteArray() - { - return byteBuf.array(); - } - - @Override - public ByteBuffer byteBuffer() - { - return byteBuf.nioBuffer(); - } - - @Override - public int capacity() - { - return byteBuf.capacity(); - } - - @Override - public void checkLimit(int limit) - { - throw new UnsupportedOperationException("checkLimit not supported"); - } - - @Override - public long getLong(int index, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - return byteBuf.getLong(index); - } - - @Override - public long getLong(int index) - { - return byteBuf.getLong(index); - } - - @Override - public int getInt(int index, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - return byteBuf.getInt(index); - } - - @Override - public int getInt(int index) - { - return byteBuf.getInt(index); - } - - @Override - public double getDouble(int index, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - return byteBuf.getDouble(index); - } - - @Override - public double getDouble(int index) - { - return byteBuf.getDouble(index); - } - - @Override - public float getFloat(int index, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - return byteBuf.getFloat(index); - } - - @Override - public float getFloat(int index) - { - return byteBuf.getFloat(index); - } - - @Override - public short getShort(int index, ByteOrder byteOrder) - { - ensureByteOrder(byteOrder); - return byteBuf.getShort(index); - } - - @Override - public short getShort(int index) - { - return byteBuf.getShort(index); - } - - @Override - public byte getByte(int index) - { - return byteBuf.getByte(index); - } - - @Override - public void getBytes(int index, byte[] dst) - { - byteBuf.getBytes(index, dst); - } - - @Override - public void getBytes(int index, byte[] dst, int offset, int length) - { - byteBuf.getBytes(index, dst, offset, length); - } - - @Override - public void getBytes(int index, MutableDirectBuffer dstBuffer, int dstIndex, int length) - { - throw new UnsupportedOperationException("getBytes(MutableDirectBuffer) not supported"); - } - - @Override - public void getBytes(int index, ByteBuffer dstBuffer, int length) - { - throw new UnsupportedOperationException("getBytes(ByteBuffer) not supported"); - } - - @Override - public void getBytes(int index, ByteBuffer dstBuffer, int dstOffset, int length) { - throw new UnsupportedOperationException("getBytes(ByteBuffer) not supported"); - } - - @Override - public String getStringUtf8(int offset, ByteOrder byteOrder) - { - final int length = getInt(offset, byteOrder); - return byteBuf.toString(offset + BitUtil.SIZE_OF_INT, length, StandardCharsets.UTF_8); - } - - @Override - public String getStringUtf8(int offset, int length) - { - return byteBuf.toString(offset, length, StandardCharsets.UTF_8); - } - - @Override - public String getStringWithoutLengthUtf8(int offset, int length) - { - return byteBuf.toString(offset, length, StandardCharsets.UTF_8); - } - - @Override - public void boundsCheck(int index, int length) - { - throw new UnsupportedOperationException("boundsCheck not supported"); - } - - @Override - public int wrapAdjustment() { - throw new UnsupportedOperationException("wrapAdjustment not supported"); - } - - private void ensureByteOrder(final ByteOrder byteOrder) - { - if (byteBuf.order() != byteOrder) - { - byteBuf.order(byteOrder); - } - } - - @Override - public void putChar(int index, char value, ByteOrder byteOrder) { - throw new UnsupportedOperationException("getBytes(MutableDirectBuffer) not supported"); - } - - @Override - public void putChar(int index, char value) { - throw new UnsupportedOperationException("getBytes(MutableDirectBuffer) not supported"); - } - - @Override - public int putStringUtf8(int offset, String value) { - throw new UnsupportedOperationException("getBytes(MutableDirectBuffer) not supported"); - } - - @Override - public int putStringUtf8(int index, String value, int maxEncodedSize) { - throw new UnsupportedOperationException("getBytes(MutableDirectBuffer) not supported"); - } - - @Override - public char getChar(int index, ByteOrder byteOrder) { - throw new UnsupportedOperationException("getBytes(MutableDirectBuffer) not supported"); - } - - @Override - public char getChar(int index) { - throw new UnsupportedOperationException("getBytes(MutableDirectBuffer) not supported"); - } - - @Override - public String getStringUtf8(int index) { - return null; - } - - @Override - public int compareTo(DirectBuffer o) { - throw new UnsupportedOperationException("compareTo not supported"); - } -} diff --git a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketFrameCodec.java b/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketFrameCodec.java deleted file mode 100644 index 6589217ae..000000000 --- a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketFrameCodec.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.transport.tcp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.util.ReferenceCountUtil; -import io.reactivesocket.Frame; - -import java.nio.ByteBuffer; - -/** - * A Codec that aids reading and writing of ReactiveSocket {@link Frame}s. - */ -public class ReactiveSocketFrameCodec extends ChannelDuplexHandler { - - private final MutableDirectByteBuf buffer = new MutableDirectByteBuf(Unpooled.buffer(0)); - private final Frame frame = Frame.allocate(buffer); - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof ByteBuf) { - try { - buffer.wrap((ByteBuf) msg); - frame.wrap(buffer, 0); - ctx.fireChannelRead(frame); - } finally { - ReferenceCountUtil.release(msg); - } - } else { - super.channelRead(ctx, msg); - } - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - if (msg instanceof Frame) { - ByteBuffer src = ((Frame)msg).getByteBuffer(); - ByteBuf toWrite = ctx.alloc().buffer(src.remaining()).writeBytes(src); - ctx.write(toWrite, promise); - } else { - super.write(ctx, msg, promise); - } - } -} diff --git a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketFrameLogger.java b/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketFrameLogger.java deleted file mode 100644 index e20ac4bcc..000000000 --- a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/ReactiveSocketFrameLogger.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.transport.tcp; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.reactivesocket.Frame; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; - -public class ReactiveSocketFrameLogger extends ChannelDuplexHandler { - - private final Logger logger; - private final Level logLevel; - - public ReactiveSocketFrameLogger(String name, Level logLevel) { - this.logLevel = logLevel; - logger = LoggerFactory.getLogger(name); - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - logFrameIfEnabled(ctx, msg, " Writing frame: "); - super.write(ctx, msg, promise); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - logFrameIfEnabled(ctx, msg, " Read frame: "); - super.channelRead(ctx, msg); - } - - private void logFrameIfEnabled(ChannelHandlerContext ctx, Object msg, String logMsgPrefix) { - if (msg instanceof Frame) { - Frame f = (Frame) msg; - switch (logLevel) { - case ERROR: - if (logger.isErrorEnabled()) { - logger.error(ctx.channel() + logMsgPrefix + f); - } - break; - case WARN: - if (logger.isWarnEnabled()) { - logger.warn(ctx.channel() + logMsgPrefix + f); - } - break; - case INFO: - if (logger.isInfoEnabled()) { - logger.info(ctx.channel() + logMsgPrefix + f); - } - break; - case DEBUG: - if (logger.isDebugEnabled()) { - logger.debug(ctx.channel() + logMsgPrefix + f); - } - break; - case TRACE: - if (logger.isTraceEnabled()) { - logger.trace(ctx.channel() + logMsgPrefix + f); - } - break; - } - } - } -} diff --git a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/TcpDuplexConnection.java b/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/TcpDuplexConnection.java deleted file mode 100644 index 39c425833..000000000 --- a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/TcpDuplexConnection.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.transport.tcp; - -import io.reactivesocket.DuplexConnection; -import io.reactivesocket.Frame; -import io.reactivex.netty.channel.Connection; -import org.reactivestreams.Publisher; - -import static rx.RxReactiveStreams.*; - -public class TcpDuplexConnection implements DuplexConnection { - - private final Connection connection; - private final Publisher closeNotifier; - private final Publisher close; - - public TcpDuplexConnection(Connection connection) { - this.connection = connection; - closeNotifier = toPublisher(connection.closeListener()); - close = toPublisher(connection.close()); - } - - @Override - public Publisher send(Publisher frames) { - return toPublisher(connection.writeAndFlushOnEach(toObservable(frames))); - } - - @Override - public Publisher receive() { - return toPublisher(connection.getInput()); - } - - @Override - public double availability() { - return connection.unsafeNettyChannel().isActive() ? 1.0 : 0.0; - } - - @Override - public Publisher close() { - return close; - } - - @Override - public Publisher onClose() { - return closeNotifier; - } - - public String toString() { - return connection.unsafeNettyChannel().toString(); - } -} diff --git a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/client/TcpTransportClient.java b/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/client/TcpTransportClient.java deleted file mode 100644 index 1f8249dc3..000000000 --- a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/client/TcpTransportClient.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.transport.tcp.client; - -import io.netty.buffer.ByteBuf; -import io.reactivesocket.DuplexConnection; -import io.reactivesocket.Frame; -import io.reactivesocket.transport.TransportClient; -import io.reactivesocket.transport.tcp.ReactiveSocketFrameCodec; -import io.reactivesocket.transport.tcp.ReactiveSocketFrameLogger; -import io.reactivesocket.transport.tcp.ReactiveSocketLengthCodec; -import io.reactivesocket.transport.tcp.TcpDuplexConnection; -import io.reactivex.netty.protocol.tcp.client.TcpClient; -import org.reactivestreams.Publisher; -import org.slf4j.event.Level; - -import java.net.SocketAddress; -import java.util.function.Function; - -import static rx.RxReactiveStreams.*; - -public class TcpTransportClient implements TransportClient { - - private final TcpClient rxNettyClient; - - public TcpTransportClient(TcpClient client) { - rxNettyClient = client; - } - - @Override - public Publisher connect() { - return toPublisher(rxNettyClient.createConnectionRequest() - .map(connection -> new TcpDuplexConnection(connection))); - } - - /** - * Configures the underlying {@link TcpClient}. - * - * @param configurator Function to transform the client. - * - * @return A new {@link TcpTransportClient} - */ - public TcpTransportClient configureClient(Function, TcpClient> configurator) { - return new TcpTransportClient(configurator.apply(rxNettyClient)); - } - - /** - * Enable logging of every frame read and written on every connection created by this client. - * - * @param name Name of the logger. - * @param logLevel Level at which the messages will be logged. - * - * @return A new {@link TcpTransportClient} - */ - public TcpTransportClient logReactiveSocketFrames(String name, Level logLevel) { - return configureClient(c -> - c.addChannelHandlerLast("reactive-socket-frame-codec", () -> new ReactiveSocketFrameLogger(name, logLevel)) - ); - } - - public static TcpTransportClient create(SocketAddress serverAddress) { - return new TcpTransportClient(_configureClient(TcpClient.newClient(serverAddress))); - } - - public static TcpTransportClient create(TcpClient client) { - return new TcpTransportClient(_configureClient(client)); - } - - private static TcpClient _configureClient(TcpClient client) { - return client.addChannelHandlerLast("length-codec", ReactiveSocketLengthCodec::new) - .addChannelHandlerLast("frame-codec", ReactiveSocketFrameCodec::new); - } -} diff --git a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/server/TcpTransportServer.java b/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/server/TcpTransportServer.java deleted file mode 100644 index b85bfde17..000000000 --- a/reactivesocket-transport-tcp/src/main/java/io/reactivesocket/transport/tcp/server/TcpTransportServer.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.transport.tcp.server; - -import io.netty.buffer.ByteBuf; -import io.reactivesocket.Frame; -import io.reactivesocket.transport.TransportServer; -import io.reactivesocket.transport.tcp.ReactiveSocketFrameCodec; -import io.reactivesocket.transport.tcp.ReactiveSocketFrameLogger; -import io.reactivesocket.transport.tcp.ReactiveSocketLengthCodec; -import io.reactivesocket.transport.tcp.TcpDuplexConnection; -import io.reactivesocket.transport.tcp.client.TcpTransportClient; -import io.reactivex.netty.channel.Connection; -import io.reactivex.netty.protocol.tcp.server.ConnectionHandler; -import io.reactivex.netty.protocol.tcp.server.TcpServer; -import org.slf4j.event.Level; -import rx.Observable; - -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -import static rx.RxReactiveStreams.*; - -public class TcpTransportServer implements TransportServer { - - private final TcpServer rxNettyServer; - - private TcpTransportServer(TcpServer rxNettyServer) { - this.rxNettyServer = rxNettyServer; - } - - @Override - public StartedServer start(ConnectionAcceptor acceptor) { - rxNettyServer.start(new ConnectionHandler() { - @Override - public Observable handle(Connection newConnection) { - TcpDuplexConnection duplexConnection = new TcpDuplexConnection(newConnection); - return toObservable(acceptor.apply(duplexConnection)); - } - }); - return new Started(); - } - - /** - * Configures the underlying server using the passed {@code configurator}. - * - * @param configurator Function to transform the underlying server. - * - * @return New instance of {@code TcpReactiveSocketServer}. - */ - public TcpTransportServer configureServer(Function, TcpServer> configurator) { - return new TcpTransportServer(configurator.apply(rxNettyServer)); - } - - /** - * Enable logging of every frame read and written on every connection accepted by this server. - * - * @param name Name of the logger. - * @param logLevel Level at which the messages will be logged. - * - * @return A new {@link TcpTransportServer} - */ - public TcpTransportServer logReactiveSocketFrames(String name, Level logLevel) { - return configureServer(c -> c.addChannelHandlerLast("reactive-socket-frame-codec", - () -> new ReactiveSocketFrameLogger(name, logLevel)) - ); - } - - public static TcpTransportServer create() { - return create(TcpServer.newServer()); - } - - public static TcpTransportServer create(int port) { - return create(TcpServer.newServer(port)); - } - - public static TcpTransportServer create(SocketAddress address) { - return create(TcpServer.newServer(address)); - } - - public static TcpTransportServer create(TcpServer rxNettyServer) { - return new TcpTransportServer(configure(rxNettyServer)); - } - - private static TcpServer configure(TcpServer rxNettyServer) { - return rxNettyServer.addChannelHandlerLast("line-codec", ReactiveSocketLengthCodec::new) - .addChannelHandlerLast("frame-codec", ReactiveSocketFrameCodec::new); - } - - private class Started implements StartedServer { - - @Override - public SocketAddress getServerAddress() { - return rxNettyServer.getServerAddress(); - } - - @Override - public int getServerPort() { - return rxNettyServer.getServerPort(); - } - - @Override - public void awaitShutdown() { - rxNettyServer.awaitShutdown(); - } - - @Override - public void awaitShutdown(long duration, TimeUnit durationUnit) { - rxNettyServer.awaitShutdown(duration, durationUnit); - } - - @Override - public void shutdown() { - rxNettyServer.shutdown(); - } - } -} diff --git a/settings.gradle b/settings.gradle index 6a871cef0..811b91389 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,7 +16,6 @@ rootProject.name='reactivesocket' include 'reactivesocket-client' -include 'reactivesocket-publishers' include 'reactivesocket-core' include 'reactivesocket-discovery-eureka' include 'reactivesocket-examples' @@ -25,4 +24,4 @@ include 'reactivesocket-spectator' include 'reactivesocket-test' include 'reactivesocket-transport-aeron' include 'reactivesocket-transport-local' -include 'reactivesocket-transport-tcp' \ No newline at end of file +include 'reactivesocket-transport-netty' \ No newline at end of file From 727264a677da2655cae7d6fd2cdc66d6ba5285f9 Mon Sep 17 00:00:00 2001 From: somasun Date: Wed, 8 Mar 2017 10:19:41 -0800 Subject: [PATCH 016/731] Resurrect old tck-driver code and get it running (#249) * import of reactivesocket-tck-drivers from older commit * My changes * Rebasing code on top of Reactor changes --- .gitignore | 3 + reactivesocket-tck-drivers/README.md | 59 ++ reactivesocket-tck-drivers/build.gradle | 28 + reactivesocket-tck-drivers/run.sh | 7 + .../tckdrivers/client/JavaClientDriver.java | 620 ++++++++++++++++++ .../tckdrivers/client/JavaTCPClient.java | 73 +++ .../tckdrivers/common/AddThread.java | 53 ++ .../tckdrivers/common/ConsoleUtils.java | 81 +++ .../tckdrivers/common/EchoSubscription.java | 77 +++ .../tckdrivers/common/MySubscriber.java | 227 +++++++ .../tckdrivers/common/ParseChannel.java | 171 +++++ .../tckdrivers/common/ParseChannelThread.java | 45 ++ .../tckdrivers/common/ParseMarble.java | 184 ++++++ .../tckdrivers/common/ParseThread.java | 37 ++ .../tckdrivers/common/ServerThread.java | 32 + .../tckdrivers/common/Tuple.java | 68 ++ .../reactivesocket/tckdrivers/main/Main.java | 57 ++ .../tckdrivers/main/TestMain.java | 57 ++ .../tckdrivers/server/JavaServerDriver.java | 280 ++++++++ .../tckdrivers/server/JavaTCPServer.java | 59 ++ .../src/test/resources/clienttest$.txt | 73 +++ .../src/test/resources/servertest$.txt | 6 + settings.gradle | 3 +- 23 files changed, 2299 insertions(+), 1 deletion(-) create mode 100644 reactivesocket-tck-drivers/README.md create mode 100644 reactivesocket-tck-drivers/build.gradle create mode 100755 reactivesocket-tck-drivers/run.sh create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/client/JavaClientDriver.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/client/JavaTCPClient.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/AddThread.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ConsoleUtils.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/EchoSubscription.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/MySubscriber.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseChannel.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseChannelThread.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseMarble.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseThread.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ServerThread.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/Tuple.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/main/Main.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/main/TestMain.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaServerDriver.java create mode 100644 reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaTCPServer.java create mode 100644 reactivesocket-tck-drivers/src/test/resources/clienttest$.txt create mode 100644 reactivesocket-tck-drivers/src/test/resources/servertest$.txt diff --git a/.gitignore b/.gitignore index 3be3ba898..4dd4cdc55 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,6 @@ atlassian-ide-plugin.xml # NetBeans specific files/directories .nbattrs /bin + +#.gitignore in subdirectory +.gitignore diff --git a/reactivesocket-tck-drivers/README.md b/reactivesocket-tck-drivers/README.md new file mode 100644 index 000000000..dc37d6ee7 --- /dev/null +++ b/reactivesocket-tck-drivers/README.md @@ -0,0 +1,59 @@ +# ReactiveSocket TCK Drivers + +This is meant to be used in conjunction with the TCK at [reactivesocket-tck](https://github.com/ReactiveSocket/reactivesocket-tck) + +## Basic Idea and Organization + +The philosophy behind the TCK is that it should allow any implementation of ReactiveSocket to verify itself against any other +implementation. In order to provide a truly polyglot solution, we determined that the best way to do so was to provide a central +TCK repository with only the code that generates the intermediate script, and then leave it up to implementers to create the +drivers for their own implementation. The script was created specifically to be easy to parse and implement drivers for, +and this Java driver is the first driver to be created as the Java implementation of ReactiveSockets is the most mature at +the time. + +The driver is organized with a simple structure. On both the client and server drivers, we have the main driver class that +do an intial parse of the script files. On the server side, this process basically constructs dynamic request handlers where +every time a request is received, the appropriate behavior is searched up and is passed to a ParseMarble object, which is run +on its own thread and is used to parse through a marble diagram and enact it's behavior. On the client side, the main driver +class splits up each test into it's own individual lines, and then runs each test synchronously in its own thread. Support +for concurrent behavior can easily be added later. + +On the client side, for the most part, each test thread just parses through each line of the test in order, synchronously and enacts its +behavior on our TestSubscriber, a special subscriber that we can use to verify that certain things have happened. `await` calls +should block the main test thread, and the test should fail if a single assert fails. + +Things get trickier with channel tests, because the client and server need to have the same behavior. In channel tests on both +sides, the driver creates a ParseChannel object, which parses through the contents of a channel tests and handles receiving +and sending data. We use the ParseMarble object to handle sending data. Here, we have one thread that continuously runs `parse()`, +and other threads that run `add()` and `request()`, which stages data to be added and requested. + + + +## Run Instructions + +You can build the project with `gradle build`. +You can run the client and server using the `run` script with `./run [options]`. The options are: + +`--server` : This is if you want to launch the server + +`--client` : This is if you want to launch the client + +`--host ` : This is for the client only, determines what host to connect to + +`--port

    ` : If launching as client, tells it to connect to port `p`, and if launching as server, tells what port to server on + +`--file ` : The path to the script file. Make sure to give the server and client the correct file formats + +`--debug` : This is if you want to look at the individual frames being sent and received by the client + +`--tests` : This allows you, when you're running the client, to specify the tests you want to run by name. Each test +should be comma separated. + +Examples: +`./run.sh --server --port 4567 --file src/test/resources/servertest.txt` should start up a server on port `4567` that +has its behavior determined by the file `servertest.txt`. + +`./run.sh --client --host localhost --port 4567 --file src/test/resources/clienttest.txt --debug --tests genericTest,badTest` should +start the client and have it connect to localhost on port `4567` and load the tests in `clienttest.txt` in debug mode, +and only run the tests named `genericTest` and `badTest`. + diff --git a/reactivesocket-tck-drivers/build.gradle b/reactivesocket-tck-drivers/build.gradle new file mode 100644 index 000000000..04b6e7657 --- /dev/null +++ b/reactivesocket-tck-drivers/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'application' +apply plugin: 'java' + +mainClassName = "io.reactivesocket.tckdrivers.main.Main" + +jar { + manifest { + attributes "Main-Class": "$mainClassName" + } + + from { + configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +dependencies { + + compile project(':reactivesocket-core') + compile project(':reactivesocket-client') + compile project(':reactivesocket-transport-netty') + testCompile project(':reactivesocket-test') + compile 'com.fasterxml.jackson.core:jackson-core:2.8.0.rc2' + compile 'com.fasterxml.jackson.core:jackson-databind:2.8.0.rc2' + compile 'org.apache.commons:commons-lang3:3.4' + compile 'io.reactivex:rxnetty-tcp:0.5.2-rc.5' + compile 'io.reactivex.rxjava2:rxjava:2.0.2' + compile 'io.airlift:airline:0.7' +} diff --git a/reactivesocket-tck-drivers/run.sh b/reactivesocket-tck-drivers/run.sh new file mode 100755 index 000000000..62ff4fde9 --- /dev/null +++ b/reactivesocket-tck-drivers/run.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +LATEST_VERSION=$(ls build/libs/reactivesocket-tck-drivers-*-SNAPSHOT.jar | sort -r | head -1) + +echo "running latest version $LATEST_VERSION" + +java -cp "$LATEST_VERSION" io.reactivesocket.tckdrivers.main.Main "$@" diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/client/JavaClientDriver.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/client/JavaClientDriver.java new file mode 100644 index 000000000..9c2d186f2 --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/client/JavaClientDriver.java @@ -0,0 +1,620 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.client; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import io.reactivesocket.Payload; +import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.tckdrivers.common.ConsoleUtils; +import io.reactivesocket.tckdrivers.common.EchoSubscription; +import io.reactivesocket.tckdrivers.common.MySubscriber; +import io.reactivesocket.tckdrivers.common.ParseChannel; +import io.reactivesocket.tckdrivers.common.ParseChannelThread; +import io.reactivesocket.tckdrivers.common.ParseMarble; +import io.reactivesocket.tckdrivers.common.Tuple; +import io.reactivesocket.util.PayloadImpl; + +/** + * This class is the driver for the Java ReactiveSocket client. To use with class with the current Java impl of + * ReactiveSocket, one should supply both a test file as well as a function that can generate ReactiveSockets on demand. + * This driver will then parse through the test file, and for each test, it will run them on their own thread and print + * out the results. + */ +public class JavaClientDriver { + + private final BufferedReader reader; + private final Map> payloadSubscribers; + private final Map> fnfSubscribers; + private final Map idToType; + private final Supplier createClient; + private final List testList; + private final String AGENT = "[CLIENT]"; + private ConsoleUtils consoleUtils = new ConsoleUtils(AGENT); + + public JavaClientDriver(String path, Supplier createClient, List tests) + throws FileNotFoundException { + this.reader = new BufferedReader(new FileReader(path)); + this.payloadSubscribers = new HashMap<>(); + this.fnfSubscribers = new HashMap<>(); + this.idToType = new HashMap<>(); + this.createClient = createClient; + this.testList = tests; + } + + private enum TestResult { + PASS, FAIL, CHANNEL + } + + /** + * Splits the test file into individual tests, and then run each of them on their own thread. + * @throws IOException + */ + public void runTests() throws IOException { + List> tests = new ArrayList<>(); + List test = new ArrayList<>(); + String line = reader.readLine(); + while (line != null) { + switch (line) { + case "!": + tests.add(test); + test = new ArrayList<>(); + break; + default: + test.add(line); + break; + } + line = reader.readLine(); + } + tests.add(test); + tests = tests.subList(1, tests.size()); // remove the first list, which is empty + for (List t : tests) { + TestThread thread = new TestThread(t); + thread.start(); + thread.join(); + } + } + + /** + * Parses through the commands for each test, and calls handlers that execute the commands. + * @param test the list of strings which makes up each test case + * @param name the name of the test + * @return an option with either true if the test passed, false if it failed, or empty if no subscribers were found + */ + private TestResult parse(List test, String name) throws Exception { + List id = new ArrayList<>(); + Iterator iter = test.iterator(); + boolean shouldPass = true; // determines whether this test is supposed to pass or fail + boolean channelTest = false; // tells whether this is a test for channel or not + boolean hasPassed = true; + while (iter.hasNext()) { + String line = iter.next(); + String[] args = line.split("%%"); + switch (args[0]) { + case "subscribe": + handleSubscribe(args); + id.add(args[2]); + break; + case "channel": + channelTest = true; + handleChannel(args, iter, name, shouldPass); + break; + case "echochannel": + handleEchoChannel(args); + break; + case "await": + switch (args[1]) { + case "terminal": + hasPassed &= handleAwaitTerminal(args); + break; + case "atLeast": + hasPassed &= handleAwaitAtLeast(args); + break; + case "no_events": + hasPassed &= handleAwaitNoEvents(args); + break; + default: + break; + } + break; + + case "assert": + switch (args[1]) { + case "no_error": + hasPassed &= assertNoError(args); + break; + case "error": + hasPassed &= assertError(args); + break; + case "received": + hasPassed &= assertReceived(args); + break; + case "received_n": + hasPassed &= assertReceivedN(args); + break; + case "received_at_least": + hasPassed &= assertReceivedAtLeast(args); + break; + case "completed": + hasPassed &= assertCompleted(args); + break; + case "no_completed": + hasPassed &= assertNoCompleted(args); + break; + case "canceled": + hasPassed &= assertCancelled(args); + break; + } + break; + case "take": + handleTake(args); + break; + case "request": + handleRequest(args); + break; + case "cancel": + handleCancel(args); + break; + case "EOF": + handleEOF(); + break; + case "pass": + shouldPass = true; + break; + case "fail": + shouldPass = false; + break; + default: + // the default behavior is to just skip the line, so we can acommodate slight changes to the TCK + break; + } + + } + // this check each of the subscribers to see that they all passed their assertions + if (id.size() > 0) { + for (String str : id) { + if (payloadSubscribers.get(str) != null) hasPassed = hasPassed && payloadSubscribers.get(str).hasPassed(); + else hasPassed = hasPassed && fnfSubscribers.get(str).hasPassed(); + } + if ((shouldPass && hasPassed) || (!shouldPass && !hasPassed)) return TestResult.PASS; + else return TestResult.FAIL; + } + else if (channelTest) return TestResult.CHANNEL; + else throw new Exception("There is no subscriber in this test"); + } + + /** + * This function takes in the arguments for the subscribe command, and subscribes an instance of MySubscriber + * with an initial request of 0 (which means don't immediately make a request) to an instance of the corresponding + * publisher + * @param args + */ + private void handleSubscribe(String[] args) { + switch (args[1]) { + case "rr": + MySubscriber rrsub = new MySubscriber<>(0L, AGENT); + payloadSubscribers.put(args[2], rrsub); + idToType.put(args[2], args[1]); + ReactiveSocket rrclient = createClient.get(); + consoleUtils.info("Sending RR with " + args[3] + " " + args[4]); + Publisher rrpub = rrclient.requestResponse(new PayloadImpl(args[3], args[4])); + rrpub.subscribe(rrsub); + break; + case "rs": + MySubscriber rssub = new MySubscriber<>(0L, AGENT); + payloadSubscribers.put(args[2], rssub); + idToType.put(args[2], args[1]); + ReactiveSocket rsclient = createClient.get(); + consoleUtils.info("Sending RS with " + args[3] + " " + args[4]); + Publisher rspub = rsclient.requestStream(new PayloadImpl(args[3], args[4])); + rspub.subscribe(rssub); + break; + case "fnf": + MySubscriber fnfsub = new MySubscriber<>(0L, AGENT); + fnfSubscribers.put(args[2], fnfsub); + idToType.put(args[2], args[1]); + ReactiveSocket fnfclient = createClient.get(); + consoleUtils.info("Sending fnf with " + args[3] + " " + args[4]); + Publisher fnfpub = fnfclient.fireAndForget(new PayloadImpl(args[3], args[4])); + fnfpub.subscribe(fnfsub); + break; + default:break; + } + } + + /** + * This function takes in an iterator that is parsing through the test, and collects all the parts that make up + * the channel functionality. It then create a thread that runs the test, which we wait to finish before proceeding + * with the other tests. + * @param args + * @param iter + * @param name + */ + private void handleChannel(String[] args, Iterator iter, String name, boolean pass) { + List commands = new ArrayList<>(); + String line = iter.next(); + // channel script should be bounded by curly braces + while (!line.equals("}")) { + commands.add(line); + line = iter.next(); + } + // set the initial payload + Payload initialPayload = new PayloadImpl(args[1], args[2]); + + // this is the subscriber that will request data from the server, like all the other test subscribers + MySubscriber testsub = new MySubscriber<>(1L, AGENT); + CountDownLatch c = new CountDownLatch(1); + + // we now create the publisher that the server will subscribe to with its own subscriber + // we want to give that subscriber a subscription that the client will use to send data to the server + ReactiveSocket client = createClient.get(); + AtomicReference mypct = new AtomicReference<>(); + Publisher pub = client.requestChannel(new Publisher() { + @Override + public void subscribe(Subscriber s) { + ParseMarble pm = new ParseMarble(s, AGENT); + TestSubscription ts = new TestSubscription(pm, initialPayload, s); + s.onSubscribe(ts); + ParseChannel pc = new ParseChannel(commands, testsub, pm, name, pass, AGENT); + ParseChannelThread pct = new ParseChannelThread(pc); + pct.start(); + mypct.set(pct); + c.countDown(); + } + }); + pub.subscribe(testsub); + try { + c.await(); + } catch (InterruptedException e) { + consoleUtils.info("interrupted"); + } + mypct.get().join(); + } + + /** + * This handles echo tests. This sets up a channel connection with the EchoSubscription, which we pass to + * the MySubscriber. + * @param args + */ + private void handleEchoChannel(String[] args) { + Payload initPayload = new PayloadImpl(args[1], args[2]); + MySubscriber testsub = new MySubscriber<>(1L, AGENT); + ReactiveSocket client = createClient.get(); + Publisher pub = client.requestChannel(new Publisher() { + @Override + public void subscribe(Subscriber s) { + EchoSubscription echoSub = new EchoSubscription(s); + s.onSubscribe(echoSub); + testsub.setEcho(echoSub); + s.onNext(initPayload); + } + }); + pub.subscribe(testsub); + } + + private boolean handleAwaitTerminal(String[] args) { + consoleUtils.info("Awaiting at Terminal"); + String id = args[2]; + if (idToType.get(id) == null) { + consoleUtils.failure("Could not find subscriber with given id"); + return false; + } else { + if (idToType.get(id).equals("fnf")) { + MySubscriber sub = fnfSubscribers.get(id); + return sub.awaitTerminalEvent(); + } else { + MySubscriber sub = payloadSubscribers.get(id); + return sub.awaitTerminalEvent(); + } + } + } + + private boolean handleAwaitAtLeast(String[] args) { + consoleUtils.info("Awaiting at Terminal for at least " + args[3]); + try { + String id = args[2]; + MySubscriber sub = payloadSubscribers.get(id); + return sub.awaitAtLeast(Long.parseLong(args[3])); + } catch (InterruptedException e) { + consoleUtils.error("interrupted"); + return false; + } + } + + private boolean handleAwaitNoEvents(String[] args) { + try { + String id = args[2]; + MySubscriber sub = payloadSubscribers.get(id); + return sub.awaitNoEvents(Long.parseLong(args[3])); + } catch (InterruptedException e) { + consoleUtils.error("Interrupted"); + return false; + } + } + + private boolean assertNoError(String[] args) { + String id = args[2]; + if (idToType.get(id) == null) { + consoleUtils.error("Could not find subscriber with given id"); + return false; + } else { + if (idToType.get(id).equals("fnf")) { + MySubscriber sub = fnfSubscribers.get(id); + try { + sub.assertNoErrors(); + return true; + } catch (Throwable ex) { + return false; + } + } else { + MySubscriber sub = payloadSubscribers.get(id); + sub.assertNoErrors(); + try { + sub.assertNoErrors(); + return true; + } catch (Throwable ex) { + return false; + } + } + } + } + + private boolean assertError(String[] args) { + consoleUtils.info("Checking for error"); + String id = args[2]; + if (idToType.get(id) == null) { + consoleUtils.error("Could not find subscriber with given id"); + return false; + } else { + if (idToType.get(id).equals("fnf")) { + MySubscriber sub = fnfSubscribers.get(id); + return sub.myAssertError(new Throwable()); + } else { + MySubscriber sub = payloadSubscribers.get(id); + return sub.myAssertError(new Throwable()); + } + } + } + + private boolean assertReceived(String[] args) { + consoleUtils.info("Verify we received " + args[3]); + String id = args[2]; + MySubscriber sub = payloadSubscribers.get(id); + String[] values = args[3].split("&&"); + List> assertList = new ArrayList<>(); + for (String v : values) { + String[] vals = v.split(","); + assertList.add(new Tuple<>(vals[0], vals[1])); + } + return sub.assertValues(assertList); + } + + private boolean assertReceivedN(String[] args) { + String id = args[2]; + MySubscriber sub = payloadSubscribers.get(id); + try { + sub.assertValueCount(Integer.parseInt(args[3])); + } catch (Throwable ex) { + return false; + } + return true; + } + + private boolean assertReceivedAtLeast(String[] args) { + String id = args[2]; + MySubscriber sub = payloadSubscribers.get(id); + return sub.assertReceivedAtLeast(Integer.parseInt(args[3])); + } + + private boolean assertCompleted(String[] args) { + consoleUtils.info("Handling onComplete"); + String id = args[2]; + if (idToType.get(id) == null) { + consoleUtils.error("Could not find subscriber with given id"); + return false; + } else { + if (idToType.get(id).equals("fnf")) { + MySubscriber sub = fnfSubscribers.get(id); + try { + sub.assertComplete(); + } catch (Throwable ex) { + return false; + } + return true; + } else { + MySubscriber sub = payloadSubscribers.get(id); + try { + sub.assertComplete(); + } catch (Throwable ex) { + return false; + } + return true; + } + } + } + + private boolean assertNoCompleted(String[] args) { + consoleUtils.info("Handling NO onComplete"); + String id = args[2]; + if (idToType.get(id) == null) { + consoleUtils.error("Could not find subscriber with given id"); + return false; + } else { + if (idToType.get(id).equals("fnf")) { + MySubscriber sub = fnfSubscribers.get(id); + try { + sub.assertNotComplete(); + } catch (Throwable ex) { + return false; + } + return true; + } else { + MySubscriber sub = payloadSubscribers.get(id); + try { + sub.assertNotComplete(); + } catch (Throwable ex) { + return false; + } + return true; + } + } + } + + private boolean assertCancelled(String[] args) { + String id = args[2]; + MySubscriber sub = payloadSubscribers.get(id); + return sub.isCancelled(); + } + + private void handleRequest(String[] args) { + Long num = Long.parseLong(args[1]); + String id = args[2]; + if (idToType.get(id) == null) { + consoleUtils.error("Could not find subscriber with given id"); + } else { + if (idToType.get(id).equals("fnf")) { + MySubscriber sub = fnfSubscribers.get(id); + consoleUtils.info("ClientDriver: Sending request for " + num); + sub.request(num); + } else { + MySubscriber sub = payloadSubscribers.get(id); + consoleUtils.info("ClientDriver: Sending request for " + num); + sub.request(num); + } + } + } + + private void handleTake(String[] args) { + String id = args[2]; + Long num = Long.parseLong(args[1]); + MySubscriber sub = payloadSubscribers.get(id); + sub.take(num); + } + + private void handleCancel(String[] args) { + String id = args[1]; + MySubscriber sub = payloadSubscribers.get(id); + sub.cancel(); + } + + private void handleEOF() { + MySubscriber fnfsub = new MySubscriber<>(0L, AGENT); + ReactiveSocket fnfclient = createClient.get(); + Publisher fnfpub = fnfclient.fireAndForget(new PayloadImpl("shutdown", "shutdown")); + fnfpub.subscribe(fnfsub); + fnfsub.request(1); + } + + /** + * This thread class parses through a single test and prints whether it succeeded or not + */ + private class TestThread implements Runnable { + private Thread t; + private List test; + private long startTime; + private long endTime; + private boolean isRun = true; + + public TestThread(List test) { + this.t = new Thread(this); + this.test = test; + } + + @Override + public void run() { + String name = ""; + name = test.get(0).split("%%")[1]; + if (testList.size() > 0 && !testList.contains(name)) { + isRun = false; + return; + } + try { + consoleUtils.teststart(name); + TestResult result = parse(test.subList(1, test.size()), name); + if (result == TestResult.PASS) + consoleUtils.success(name); + else if (result == TestResult.FAIL) + consoleUtils.failure(name); + } catch (Exception e) { + e.printStackTrace(); + consoleUtils.failure(name); + } + } + + public void start() { + startTime = System.nanoTime(); + t.start(); + } + + public void join() { + try { + t.join(); + endTime = System.nanoTime(); + if (isRun) consoleUtils.time((endTime - startTime)/1000000.0 + " MILLISECONDS\n"); + } catch(Exception e) { + consoleUtils.error("join exception"); + } + } + + } + + /** + * A subscription for channel, it handles request(n) by sort of faking an initial payload. + */ + private class TestSubscription implements Subscription { + private boolean firstRequest = true; + private ParseMarble pm; + private Payload initPayload; + private Subscriber sub; + + public TestSubscription(ParseMarble pm, Payload initpayload, Subscriber sub) { + this.pm = pm; + this.initPayload = initpayload; + this. sub = sub; + } + + @Override + public void cancel() { + pm.cancel(); + } + + @Override + public void request(long n) { + consoleUtils.info("TestSubscription: request " + n); + long m = n; + if (firstRequest) { + sub.onNext(initPayload); + firstRequest = false; + m = m - 1; + } + if (m > 0) pm.request(m); + } + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/client/JavaTCPClient.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/client/JavaTCPClient.java new file mode 100644 index 000000000..b4c89d12f --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/client/JavaTCPClient.java @@ -0,0 +1,73 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.client; + +import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.transport.netty.client.TcpTransportClient; + +import io.reactivesocket.client.ReactiveSocketClient; +import io.reactivex.Flowable; +import io.reactivesocket.client.KeepAliveProvider; +import io.reactivesocket.client.SetupProvider; + +import reactor.ipc.netty.tcp.TcpClient; + +import java.net.*; +import java.util.List; + + +/** + * A client that implements a method to create ReactiveSockets, and runs the tests. + */ +public class JavaTCPClient { + + private static URI uri; + + public void run(String realfile, String host, int port, boolean debug2, List tests) + throws MalformedURLException, URISyntaxException { + // we pass in our reactive socket here to the test suite + String file = "reactivesocket-tck-drivers/src/test/resources/clienttest$.txt"; + if (realfile != null) file = realfile; + try { + setURI(new URI("tcp://" + host + ":" + port + "/rs")); + JavaClientDriver jd = new JavaClientDriver(file, JavaTCPClient::createClient, tests); + jd.runTests(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void setURI(URI uri2) { + uri = uri2; + } + + /** + * A function that creates a ReactiveSocket on a new TCP connection. + * @return a ReactiveSocket + */ + public static ReactiveSocket createClient() { + if ("tcp".equals(uri.getScheme())) { + SocketAddress address = new InetSocketAddress(uri.getHost(), uri.getPort()); + ReactiveSocketClient client = ReactiveSocketClient.create(TcpTransportClient.create(TcpClient.create(options -> + options.connect((InetSocketAddress)address))), + SetupProvider.keepAlive(KeepAliveProvider.never()).disableLease()); + ReactiveSocket socket = Flowable.fromPublisher(client.connect()).singleOrError().blockingGet(); + return socket; + } + else { + throw new UnsupportedOperationException("uri unsupported: " + uri); + } + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/AddThread.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/AddThread.java new file mode 100644 index 000000000..4c05ac5fd --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/AddThread.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.common; + +import java.util.concurrent.CountDownLatch; + +/** + * A thread that is created to wait to be able to add a marble string. We wait for the previous thread to have finished + * adding before allowing this thread to add, and after adding, we call countDown() to allow whatever thread waiting + * on this one to begin adding + */ +public class AddThread implements Runnable { + + private String marble; + private ParseMarble parseMarble; + private Thread t; + private CountDownLatch prev, curr; + + public AddThread(String marble, ParseMarble parseMarble, CountDownLatch prev, CountDownLatch curr) { + this.marble = marble; + this.parseMarble = parseMarble; + this.t = new Thread(this); + this.prev = prev; + this.curr = curr; + } + + @Override + public void run() { + try { + // await for the previous latch to have counted down, if it exists + if (prev != null) prev.await(); + parseMarble.add(marble); + curr.countDown(); // count down on the current to unblock the next add + } catch (InterruptedException e) { + System.out.println("Interrupted in AddThread"); + } + } + + public void start() { + t.start(); + } +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ConsoleUtils.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ConsoleUtils.java new file mode 100644 index 000000000..fa5b1cf99 --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ConsoleUtils.java @@ -0,0 +1,81 @@ +package io.reactivesocket.tckdrivers.common; + +/** + * This class handles everything that gets printed to the console + */ +public class ConsoleUtils { + + private static final String ANSI_RESET = ""; //"\u001B[0m"; + private static final String ANSI_RED = ""; //\u001B[31m"; + private static final String ANSI_GREEN = ""; //\u001B[32m"; + private static final String ANSI_CYAN = ""; //\u001B[36m"; + private static final String ANSI_BLUE = ""; //\u001B[34m"; + private static boolean allPassed = true; + private String agent; + + public ConsoleUtils(String s) { + agent = s + " "; + } + + /** + * Logs something at the info level + */ + public void info(String s) { + System.out.println(agent + "INFO: " + s); + } + + /** + * Logs a successful event + */ + public void success(String s) { + System.out.println(ANSI_GREEN + agent + "SUCCESS: " + s + ANSI_RESET); + } + + /** + * Logs a failure event, and sets the allPassed boolean in this class to false. This can be used to check if there + * have been any failures in any tests after all tests have been run by the driver. + */ + public void failure(String s) { + allPassed = false; + System.out.println(ANSI_RED + agent + "FAILURE: " + s + ANSI_RESET); + } + + /** + * Logs an error event, and sets the allPassed boolean in this class to false. This can be used to check if there + * have been any failures in any tests after all tests have been run by the driver. + */ + public void error(String s) { + allPassed = false; + System.out.println(agent + "ERROR: " + s); + } + + /** + * Logs a time + */ + public void time(String s) { + System.out.println(ANSI_CYAN + agent + "TIME: " + s + ANSI_RESET); + } + + /** + * Logs the initial payload the server has received + */ + public void initialPayload(String s) { + System.out.println(agent + s); + } + + /** + * Logs the start of a test + */ + public void teststart(String s) { + System.out.println(ANSI_BLUE + agent + "TEST STARTING: " + s + ANSI_RESET); + } + + /** + * Returns whether or not all tests up to the point this method is called, has passed + * @return false if there has been any failure or error, true if everything has passed + */ + public static boolean allPassed() { + return allPassed; + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/EchoSubscription.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/EchoSubscription.java new file mode 100644 index 000000000..7d02be26d --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/EchoSubscription.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.common; + +import io.reactivesocket.Payload; +import io.reactivesocket.util.PayloadImpl; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * This class is a special subscription that allows us to implement echo tests without having to use ay complex + * complex Rx constructs. Subscriptions handle the sending of data to whoever is requesting it, this subscription + * has a very basic implementation of a backpressurebuffer that allows for flow control, and allows that the rate at + * which elements are produced to it can differ from the rate at which they are consumed. + * + * This class should be passed inside of MySubscriber when one wants to do an echo test, so that all the values + * that the MySubscriber receives immediately gets buffered here and prepared to be sent. This implementation is + * needed because we want to send both the exact same data and metadata. If we used our ParseMarble class, we could + * add a function to allow dynamic changing of our argMap object, but even then, there are only small finite number + * of characters we can use in the marble diagram. + */ +public class EchoSubscription implements Subscription { + + /** + * This is our backpressure buffer + */ + private Queue> q; + private long numSent = 0; + private long numRequested = 0; + private Subscriber sub; + private boolean cancelled = false; + + public EchoSubscription(Subscriber sub) { + q = new ConcurrentLinkedQueue<>(); + this.sub = sub; + } + + /** + * Every time our buffer grows, if there are still requests to satisfy, we need to send as much as we can. + * We make this synchronized so we can avoid data races. + * @param payload + */ + public void add(Tuple payload) { + q.add(payload); + if (numSent < numRequested) request(0); + } + + @Override + public synchronized void request(long n) { + numRequested += n; + while (numSent < numRequested && !q.isEmpty() && !cancelled) { + Tuple tup = q.poll(); + System.out.println("Sending ... " + tup); + sub.onNext(new PayloadImpl(tup.getK(), tup.getV())); + numSent++; + } + } + + @Override + public void cancel() { + cancelled = true; + } +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/MySubscriber.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/MySubscriber.java new file mode 100644 index 000000000..525259c3a --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/MySubscriber.java @@ -0,0 +1,227 @@ + +package io.reactivesocket.tckdrivers.common; + +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.reactivestreams.*; + +import io.reactivesocket.Frame; +import io.reactivesocket.Payload; +import io.reactivesocket.frame.ByteBufferUtil; + +import io.reactivex.subscribers.TestSubscriber; + + +public class MySubscriber extends TestSubscriber { + + private ConsoleUtils consoleUtils; + + private long maxAwait = 5000; + /** + * this will be locked everytime we await at most some number of myValues, the await will always be with a timeout + * After the timeout, we look at the value inside the countdown latch to make sure we counted down the + * number of myValues we expected + */ + private CountDownLatch numOnNext = new CountDownLatch(Integer.MAX_VALUE); + /** + * This latch handles the logic in take. + */ + private CountDownLatch takeLatch = new CountDownLatch(Integer.MAX_VALUE); + /** + * Keeps track if this test subscriber is myPassing + */ + private boolean isPassing = true; + + private boolean isComplete = false; + + private EchoSubscription echosub; + + private boolean isEcho = false; + + public MySubscriber(long initialRequest, String agent) { + super(initialRequest); + this.consoleUtils = new ConsoleUtils(agent); + } + + @Override + public void onSubscribe(Subscription s) { + consoleUtils.info("MySubscriber: onSubscribe()"); + super.onSubscribe(s); + } + + @Override + public void onNext(T t) { + Payload p = (Payload) t; + Tuple tup = new Tuple<>(ByteBufferUtil.toUtf8String(p.getData()), + ByteBufferUtil.toUtf8String(p.getMetadata())); + consoleUtils.info("On NEXT got : " + tup.getK() + " " + tup.getV()); + if (isEcho) { + echosub.add(tup); + return; + } + super.onNext(t); + numOnNext.countDown(); + takeLatch.countDown(); + } + + public final boolean awaitAtLeast(long n) throws InterruptedException { + int waitIterations = 0; + while (valueCount() < n) { + if (waitIterations * 100 >= maxAwait) { + myFail("Await at least timed out"); + break; + } + numOnNext.await(100, TimeUnit.MILLISECONDS); + waitIterations++; + } + myPass("Got " + valueCount() + " out of " + n + " values expected"); + numOnNext = new CountDownLatch(Integer.MAX_VALUE); + return true; + } + + // could potentially have a race condition, but cancel is asynchronous anyways + public final boolean awaitNoEvents(long time) throws InterruptedException { + int nummyValues = values.size(); + boolean iscanceled = isCancelled(); + boolean iscompleted = isComplete; + Thread.sleep(time); + if (nummyValues == values.size() && iscanceled == isCancelled() && iscompleted == isComplete) { + myPass("No additional events"); + return true; + } else { + myFail("Received additional events"); + return false; + } + } + + public final boolean myAssertError(Throwable error) { + String prefix = ""; + if (done.getCount() != 0) { + prefix = "Subscriber still running! "; + } + int s = errors.size(); + if (s == 0) { + myFail(prefix + "No errors"); + return true; + } + myPass("Error received"); + return true; + } + + public final boolean assertValues(List> values) { + String prefix = ""; + if (done.getCount() != 0) { + prefix = "Subscriber still running! "; + } + assertReceivedAtLeast(values.size()); + for (int i = 0; i < values.size(); i++) { + Frame p = (Frame) values().get(i); + try { + // TODO (somasun) : debug why this occurs + p.getType(); + } catch (Exception ex) { + System.out.println("Undefined Payload. Skipping"); + continue; + } + Tuple v = new Tuple<>(ByteBufferUtil.toUtf8String(p.getData()), + ByteBufferUtil.toUtf8String(p.getMetadata())); + Tuple u = values.get(i); + if (!Objects.equals(u, v)) { + myFail(prefix + "Values at position " + i + " differ; Expected: " + + valueAndClass(u) + ", Actual: " + valueAndClass(v)); + myFail("value does not match"); + return false; + } + } + myPass("All values match"); + return true; + } + + public final void assertValue(Tuple value) { + String prefix = ""; + if (done.getCount() != 0) { + prefix = "Subscriber still running! "; + } + int s = this.values.size(); + if (s != 1) { + myFail(prefix + "Expected: 1, Actual: " + valueCount()); + myFail("value does not match"); + } + Payload p = (Payload) values().get(0); + Tuple v = new Tuple<>(ByteBufferUtil.toUtf8String(p.getData()), + ByteBufferUtil.toUtf8String(p.getMetadata())); + if (!Objects.equals(value, v)) { + myFail(prefix + "Expected: " + valueAndClass(value) + ", Actual: " + valueAndClass(v)); + myFail("value does not match"); + } + myPass("Value matches"); + } + + public boolean assertReceivedAtLeast(int count) { + String prefix = ""; + if (done.getCount() != 0) { + prefix = "Subscriber still running! "; + } + int s = values.size(); + if (s < count) { + myFail(prefix + "Received less; Expected at least: " + count + ", Actual: " + s); + return false; + } + myPass("Received " + s + " myValues"); + return true; + } + + private void myFail(String message) { + isPassing = false; + consoleUtils.info("FAILED: " + message); + } + + private void myPass(String message) { + consoleUtils.info("PASSED: " + message); + } + + public boolean hasPassed() { + return isPassing; + } + + // there might be a race condition with take, so this behavior is defined as: either wait until we have received n + // myValues and then cancel, or cancel if we already have n myValues + public final void take(long n) { + if(values.size() >= n) { + // if we've already received at least n myValues, then we cancel + cancel(); + return; + } + int waitIterations = 0; + while(Integer.MAX_VALUE - takeLatch.getCount() < n) { + try { + // we keep track of how long we've waited for + if (waitIterations * 100 >= maxAwait) { + fail("Timeout in take"); + break; + } + takeLatch.await(100, TimeUnit.MILLISECONDS); + waitIterations++; + } catch (Exception e) { + consoleUtils.error("interrupted"); + } + } + } + + public Tuple getElement(int n) { + assert(n < values.size()); + Payload p = (Payload) values().get(n); + Tuple tup = new Tuple<>(ByteBufferUtil.toUtf8String(p.getData()), + ByteBufferUtil.toUtf8String(p.getMetadata())); + return tup; + } + + public final void setEcho(EchoSubscription echosub) { + isEcho = true; + this.echosub = echosub; + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseChannel.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseChannel.java new file mode 100644 index 000000000..baf7726c2 --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseChannel.java @@ -0,0 +1,171 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.common; + +import io.reactivesocket.Payload; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +/** + * This class is exclusively used to parse channel commands on both the client and the server + */ +public class ParseChannel { + + private List commands; + private MySubscriber sub; + private ParseMarble parseMarble; + private String name = ""; + private CountDownLatch prevRespondLatch; + private CountDownLatch currentRespondLatch; + private boolean pass = true; + public ConsoleUtils consoleUtils; + + public ParseChannel(List commands, MySubscriber sub, ParseMarble parseMarble, String agent) { + this.commands = commands; + this.sub = sub; + this.parseMarble = parseMarble; + ParseThread parseThread = new ParseThread(parseMarble); + parseThread.start(); + consoleUtils = new ConsoleUtils(agent); + } + + public ParseChannel(List commands, MySubscriber sub, ParseMarble parseMarble, + String name, boolean pass, String agent) { + this.commands = commands; + this.sub = sub; + this.parseMarble = parseMarble; + this.name = name; + ParseThread parseThread = new ParseThread(parseMarble); + parseThread.start(); + this.pass = pass; + consoleUtils = new ConsoleUtils(agent); + } + + /** + * This parses through each line of the marble test and executes the commands in each line + * Most of the functionality is the same as the switch statement in the JavaClientDriver, but this also + * allows for the channel to stage items to emit. + */ + public void parse() { + for (String line : commands) { + String[] args = line.split("%%"); + switch (args[0]) { + case "respond": + handleResponse(args); + break; + case "await": + switch (args[1]) { + case "terminal": + sub.awaitTerminalEvent(); + break; + case "atLeast": + try { + sub.awaitAtLeast(Long.parseLong(args[3])); + } catch (InterruptedException e) { + consoleUtils.error("interrupted"); + } + break; + case "no_events": + try { + sub.awaitNoEvents(Long.parseLong(args[3])); + } catch (InterruptedException e) { + consoleUtils.error("interrupted"); + } + break; + } + break; + case "assert": + switch (args[1]) { + case "no_error": + sub.assertNoErrors(); + break; + case "error": + sub.assertError(new Throwable()); + break; + case "received": + handleReceived(args); + break; + case "received_n": + sub.assertValueCount(Integer.parseInt(args[3])); + break; + case "received_at_least": + sub.assertReceivedAtLeast(Integer.parseInt(args[3])); + break; + case "completed": + sub.assertComplete(); + break; + case "no_completed": + sub.assertNotComplete(); + break; + case "canceled": + sub.isCancelled(); + break; + } + break; + case "take": + sub.take(Long.parseLong(args[1])); + break; + case "request": + sub.request(Long.parseLong(args[1])); + consoleUtils.info("requesting " + args[1]); + break; + case "cancel": + sub.cancel(); + break; + } + } + if (name.equals("")) { + name = "CHANNEL"; + } + if (sub.hasPassed() && this.pass) consoleUtils.success(name); + else if (!sub.hasPassed() && !this.pass) consoleUtils.success(name); + else consoleUtils.failure(name); + } + + /** + * On handling a command to respond with something, we create an AddThread and pass in latches to make sure + * that we don't let this thread request to add something before the previous thread has added something. + * @param args + */ + private void handleResponse(String[] args) { + consoleUtils.info("responding " + args); + if (currentRespondLatch == null) currentRespondLatch = new CountDownLatch(1); + AddThread addThread = new AddThread(args[1], parseMarble, prevRespondLatch, currentRespondLatch); + prevRespondLatch = currentRespondLatch; + currentRespondLatch = new CountDownLatch(1); + addThread.start(); + } + + /** + * This verifies that the data received by our MySubscriber matches what we expected + * @param args + */ + private void handleReceived(String[] args) { + String[] values = args[3].split("&&"); + if (values.length == 1) { + String[] temp = values[0].split(","); + sub.assertValue(new Tuple<>(temp[0], temp[1])); + } else if (values.length > 1) { + List> assertList = new ArrayList<>(); + for (String v : values) { + String[] vals = v.split(","); + assertList.add(new Tuple<>(vals[0], vals[1])); + } + sub.assertValues(assertList); + } + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseChannelThread.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseChannelThread.java new file mode 100644 index 000000000..982b28a2d --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseChannelThread.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.common; + +/** + * This thread parses through channel tests + */ +public class ParseChannelThread implements Runnable { + + private ParseChannel pc; + private Thread t; + + public ParseChannelThread(ParseChannel pc) { + this.pc = pc; + this.t = new Thread(this); + } + + @Override + public void run() { + pc.parse(); + } + + public void start() { + t.start(); + } + + public void join() { + try { + t.join(); + } catch (InterruptedException e) { + this.pc.consoleUtils.error("interrupted"); + } + } +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseMarble.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseMarble.java new file mode 100644 index 000000000..575c36bc8 --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseMarble.java @@ -0,0 +1,184 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.common; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.reactivesocket.Payload; +import io.reactivesocket.util.PayloadImpl; +import org.reactivestreams.Subscriber; + +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; + +/** + * This class parses through a marble diagram, but also implements a backpressure buffer so that the rate at + * which producers add values can be much faster than the rate at which consumers consume values. + * The backpressure buffer is the marble queue. The add function synchronously grows the marble queue, and the + * request function synchronously increments the data requested as well as unblocks the latches that are basically + * preventing the parse() method from emitting data in the backpressure buffer that it should not. + */ +public class ParseMarble { + + private Queue marble; + private Subscriber s; + private boolean cancelled = false; + private Map> argMap; + private long numSent = 0; + private long numRequested = 0; + private CountDownLatch parseLatch; + private CountDownLatch sendLatch; + private ConsoleUtils consoleUtils; + + /** + * This constructor is useful if one already has the entire marble diagram before hand, so add() does not need to + * be called. + * @param marble the whole marble diagram + * @param s the subscriber + */ + public ParseMarble(String marble, Subscriber s, String agent) { + this.s = s; + this.marble = new ConcurrentLinkedQueue<>(); + if (marble.contains("&&")) { + String[] temp = marble.split("&&"); + marble = temp[0]; + ObjectMapper mapper = new ObjectMapper(); + try { + argMap = mapper.readValue(temp[1], new TypeReference>>() { + }); + } catch (Exception e) { + System.out.println("couldn't convert argmap"); + } + } + // we want to filter out and disregard '-' since we don't care about time + for (char c : marble.toCharArray()) { + if (c != '-') this.marble.add(c); + } + parseLatch = new CountDownLatch(1); + sendLatch = new CountDownLatch(1); + consoleUtils = new ConsoleUtils(agent); + } + + /** + * This constructor is useful for channel, when the marble diagram will be build incrementally. + * @param s the subscriber + */ + public ParseMarble(Subscriber s, String agent) { + this.s = s; + this.marble = new ConcurrentLinkedQueue<>(); + parseLatch = new CountDownLatch(1); + sendLatch = new CountDownLatch(1); + consoleUtils = new ConsoleUtils(agent); + } + + /** + * This method is synchronized because we don't want two threads to try to add to the string at once. + * Calling this method also unblocks the parseLatch, which allows non-emittable symbols to be sent. In other words, + * it allows onNext and onComplete to be sent even if we've sent all the values we've been requested of. + * @param m + */ + public synchronized void add(String m) { + consoleUtils.info("adding " + m); + for (char c : m.toCharArray()) { + if (c != '-') this.marble.add(c); + } + if (!marble.isEmpty()) parseLatch.countDown(); + } + + /** + * This method is synchronized because we only want to process one request at one time. Calling this method unblocks + * the sendLatch as well as the parseLatch if we have more requests, + * as it allows both emitted and non-emitted symbols to be sent, + * @param n + */ + public synchronized void request(long n) { + numRequested += n; + if (!marble.isEmpty()) { + parseLatch.countDown(); + } + if (n > 0) sendLatch.countDown(); + } + + /** + * This function calls parse and executes the specified behavior in each line of commands + */ + public void parse() { + try { + // if cancel has been called, don't do anything + if (cancelled) return; + while (true) { + if (marble.isEmpty()) { + synchronized (parseLatch) { + if (parseLatch.getCount() == 0) parseLatch = new CountDownLatch(1); + parseLatch.await(); + } + parseLatch = new CountDownLatch(1); + } + char c = marble.poll(); + switch (c) { + case '|': + s.onComplete(); + consoleUtils.info("On complete sent"); + break; + case '#': + s.onError(new Throwable()); + consoleUtils.info("On error sent"); + break; + default: + if (numSent >= numRequested) { + synchronized (sendLatch) { + if (sendLatch.getCount() == 0) sendLatch = new CountDownLatch(1); + sendLatch.await(); + } + sendLatch = new CountDownLatch(1); + } + consoleUtils.info("numSent " + numSent + ": numRequested " + numRequested); + if (argMap != null) { + // this is hacky, but we only expect one key and one value + Map tempMap = argMap.get(c + ""); + if (tempMap == null) { + s.onNext(new PayloadImpl(c + "", c + "")); + consoleUtils.info("DATA SENT " + c + ", " + c); + } else { + List key = new ArrayList<>(tempMap.keySet()); + List value = new ArrayList<>(tempMap.values()); + s.onNext(new PayloadImpl(key.get(0), value.get(0))); + consoleUtils.info("DATA SENT " + key.get(0) + ", " + value.get(0)); + } + } else { + this.s.onNext(new PayloadImpl(c + "", c + "")); + consoleUtils.info("DATA SENT " + c + ", " + c); + } + + numSent++; + break; + } + } + } catch (InterruptedException e) { + consoleUtils.error("interrupted"); + } + + } + + /** + * Since cancel is async, it just means that we will eventually, and rather quickly, stop emitting values. + * We do this to follow the reactive streams specifications that cancel should mean that the observable eventually + * stops emitting items. + */ + public void cancel() { + cancelled = true; + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseThread.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseThread.java new file mode 100644 index 000000000..03f9ae0c7 --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ParseThread.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.common; + +/** + * This thread calls parse on the parseMarble object. + */ +public class ParseThread implements Runnable { + + private ParseMarble pm; + private Thread t; + + public ParseThread(ParseMarble pm) { + this.pm = pm; + this.t = new Thread(this); + } + + @Override + public void run() { + pm.parse(); + } + + public void start() { + t.start(); + } +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ServerThread.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ServerThread.java new file mode 100644 index 000000000..670aa2b18 --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/ServerThread.java @@ -0,0 +1,32 @@ +package io.reactivesocket.tckdrivers.common; + +import io.reactivesocket.tckdrivers.server.JavaTCPServer; + +public class ServerThread implements Runnable { + private Thread t; + private int port; + private String serverfile; + private JavaTCPServer server; + + public ServerThread(int port, String serverfile) { + t = new Thread(this); + this.port = port; + this.serverfile = serverfile; + server = new JavaTCPServer(); + } + + + @Override + public void run() { + server.run(serverfile, port); + } + + public void start() { + t.start(); + } + + public void awaitStart() { + server.awaitStart(); + } + +} \ No newline at end of file diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/Tuple.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/Tuple.java new file mode 100644 index 000000000..083eaf645 --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/Tuple.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.common; + +import org.apache.commons.lang3.builder.HashCodeBuilder; + +/** + * Simple implementation of a tuple + * @param + * @param + */ +public class Tuple { + + private final K k; + private final V v; + + public Tuple(K k, V v) { + this.k = k; + this.v = v; + } + + /** + * Returns K + * @return K + */ + public K getK() { + return this.k; + } + + /** + * Returns V + * @return V + */ + public V getV() { + return this.v; + } + + @Override + public boolean equals(Object o) { + if (!o.getClass().isInstance(this)) { + return false; + } + @SuppressWarnings("unchecked") + Tuple temp = (Tuple) o; + return temp.getV().equals(this.getV()) && temp.getK().equals(this.getK()); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(this.getK().hashCode()).append(this.getV().hashCode()).toHashCode(); + } + + @Override + public String toString() { + return getV().toString() + "," + getK().toString(); + } +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/main/Main.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/main/Main.java new file mode 100644 index 000000000..8b88405bf --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/main/Main.java @@ -0,0 +1,57 @@ +package io.reactivesocket.tckdrivers.main; + +import io.airlift.airline.Command; +import io.airlift.airline.Option; +import io.airlift.airline.SingleCommand; +import io.reactivesocket.tckdrivers.client.JavaTCPClient; +import io.reactivesocket.tckdrivers.server.JavaTCPServer; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * This class is used to run both the server and the client, depending on the options given + */ +@Command(name = "reactivesocket-driver", description = "This runs the client and servers that use the driver") +public class Main { + + @Option(name = "--debug", description = "set if you want frame level output") + public static boolean debug; + + @Option(name = "--server", description = "set if you want to run the server") + public static boolean server; + + @Option(name = "--client", description = "set if you want to run the client") + public static boolean client; + + @Option(name = "--host", description = "The host to connect to for the client") + public static String host; + + @Option(name = "--port", description = "The port") + public static int port; + + @Option(name = "--file", description = "The script file to parse, make sure to give the client and server the " + + "correct files") + public static String file; + + @Option(name = "--tests", description = "For the client only, optional argument to list out the tests you" + + " want to run, should be comma separated names") + + public static String tests; + + public static void main(String[] args) { + SingleCommand

    cmd = SingleCommand.singleCommand(Main.class); + cmd.parse(args); + if (server) { + new JavaTCPServer().run(file, port); + } else if (client) { + try { + if (tests != null) new JavaTCPClient().run(file, host, port, debug, Arrays.asList(tests.split(","))); + else new JavaTCPClient().run(file, host, port, debug, new ArrayList<>()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/main/TestMain.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/main/TestMain.java new file mode 100644 index 000000000..ff115a521 --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/main/TestMain.java @@ -0,0 +1,57 @@ +package io.reactivesocket.tckdrivers.main; + +import java.util.ArrayList; +import java.util.Arrays; + +import io.airlift.airline.Command; +import io.airlift.airline.Option; +import io.airlift.airline.SingleCommand; +import io.reactivesocket.tckdrivers.client.JavaTCPClient; +import io.reactivesocket.tckdrivers.common.ConsoleUtils; +import io.reactivesocket.tckdrivers.common.ServerThread; + +/** + * This class fires up both the client and the server, is used for the Gradle task to run the tests + */ +@Command(name = "reactivesocket-test-driver", description = "This runs the client and servers that use the driver") +public class TestMain { + + @Option(name = "--debug", description = "set if you want frame level output") + public static boolean debug; + + @Option(name = "--port", description = "The port") + public static int port; + + @Option(name = "--serverfile", description = "The script file to parse, make sure to give the server the " + + "correct file") + public static String serverfile; + + @Option(name = "--clientfile", description = "The script file for the client to parse") + public static String clientfile; + + @Option(name = "--tests", description = "For the client only, optional argument to list out the tests you" + + " want to run, should be comma separated names") + public static String tests; + + public static void main(String[] args) { + SingleCommand cmd = SingleCommand.singleCommand(TestMain.class); + cmd.parse(args); + ServerThread st = new ServerThread(port, serverfile); + st.start(); + st.awaitStart(); + try { + if (tests != null) new JavaTCPClient().run(clientfile, "localhost", port, debug, Arrays.asList(tests.split(","))); + else new JavaTCPClient().run(clientfile, "localhost", port, debug, new ArrayList<>()); + } catch (Exception e) { + e.printStackTrace(); + } + if (ConsoleUtils.allPassed()) { + System.out.println("ALL TESTS PASSED"); + System.exit(0); + } else { + System.out.println("SOME TESTS FAILED"); + System.exit(1); // exit with code 1 so that the gradle build process fails + } + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaServerDriver.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaServerDriver.java new file mode 100644 index 000000000..55c6d4cfa --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaServerDriver.java @@ -0,0 +1,280 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.server; + +import io.reactivesocket.AbstractReactiveSocket; +import io.reactivesocket.ConnectionSetupPayload; +import io.reactivesocket.Payload; +import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.frame.ByteBufferUtil; +import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; +import io.reactivesocket.lease.LeaseEnforcingSocket; +import io.reactivesocket.tckdrivers.common.*; +import io.reactivesocket.transport.netty.server.TcpTransportServer; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscription; + +import io.reactivesocket.server.ReactiveSocketServer; +import io.reactivesocket.server.ReactiveSocketServer.SocketAcceptor; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.CountDownLatch; + +/** + * This is the driver for the server. + */ +public class JavaServerDriver { + + // these map initial payload -> marble, which dictates the behavior of the server + private Map, String> requestResponseMarbles; + private Map, String> requestStreamMarbles; + private Map, String> requestSubscriptionMarbles; + // channel doesn't have an initial payload, but maybe the first payload sent can be viewed as the "initial" + private Map, List> requestChannelCommands; + private Set> requestChannelFail; + private Set> requestEchoChannel; + // first try to implement single channel subscriber + private BufferedReader reader; + // the instance of the server so we can shut it down + private TcpTransportServer server; + private TcpTransportServer.StartedServer startedServer; + private CountDownLatch waitStart; + private ConsoleUtils consoleUtils = new ConsoleUtils("[SERVER]"); + + public JavaServerDriver(String path) { + requestResponseMarbles = new HashMap<>(); + requestStreamMarbles = new HashMap<>(); + requestSubscriptionMarbles = new HashMap<>(); + requestChannelCommands = new HashMap<>(); + requestEchoChannel = new HashSet<>(); + try { + reader = new BufferedReader(new FileReader(path)); + } catch (Exception e) { + consoleUtils.error("File not found"); + } + requestChannelFail = new HashSet<>(); + } + + // should be used if we want the server to be shutdown upon receiving some EOF packet + public JavaServerDriver(String path, TcpTransportServer server, CountDownLatch waitStart) { + this(path); + this.server = server; + this.waitStart = waitStart; + requestChannelFail = new HashSet<>(); + } + + /** + * Starts up the server + */ + public void run() { + this.parse(); + ReactiveSocketServer s = ReactiveSocketServer.create(this.server); + this.startedServer = s.start(new SocketAcceptorImpl()); + waitStart.countDown(); // notify that this server has started + startedServer.awaitShutdown(); + } + + class SocketAcceptorImpl implements SocketAcceptor { + @Override + public LeaseEnforcingSocket accept(ConnectionSetupPayload setupPayload, ReactiveSocket reactiveSocket) { + return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { + @Override + public Flux requestChannel(Publisher payloads) { + return Flux.from(s -> { + try { + MySubscriber sub = new MySubscriber<>(0L, "[SERVER]"); + payloads.subscribe(sub); + // want to get equivalent of "initial payload" so we can route behavior, this might change in the future + sub.request(1); + sub.awaitAtLeast(1); + Tuple initpayload = new Tuple<>(sub.getElement(0).getK(), sub.getElement(0).getV()); + consoleUtils.initialPayload("Received Channel " + initpayload.getK() + " " + initpayload.getV()); + // if this is a normal channel handler, then initiate the normal setup + if (requestChannelCommands.containsKey(initpayload)) { + ParseMarble pm = new ParseMarble(s, "[SERVER]"); + s.onSubscribe(new TestSubscription(pm)); + ParseChannel pc; + if (requestChannelFail.contains(initpayload)) + pc = new ParseChannel(requestChannelCommands.get(initpayload), sub, pm, "CHANNEL", false, "[SERVER]"); + else + pc = new ParseChannel(requestChannelCommands.get(initpayload), sub, pm, "[SERVER]"); + ParseChannelThread pct = new ParseChannelThread(pc); + pct.start(); + } else if (requestEchoChannel.contains(initpayload)) { + EchoSubscription echoSubscription = new EchoSubscription(s); + s.onSubscribe(echoSubscription); + sub.setEcho(echoSubscription); + sub.request(10000); // request a large number, which basically means the client can send whatever + } else { + consoleUtils.error("Request channel payload " + initpayload.getK() + " " + initpayload.getV() + + "has no handler"); + } + + } catch (Exception e) { + consoleUtils.failure("Interrupted"); + } + }); + } + + @Override + public final Mono fireAndForget(Payload payload) { + return Mono.from(s -> { + Tuple initialPayload = new Tuple<>(ByteBufferUtil.toUtf8String(payload.getData()), + ByteBufferUtil.toUtf8String(payload.getMetadata())); + consoleUtils.initialPayload("Received firenforget " + initialPayload.getK() + " " + initialPayload.getV()); + if (initialPayload.getK().equals("shutdown") && initialPayload.getV().equals("shutdown")) { + try { + Thread.sleep(2000); + startedServer.shutdown(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @Override + public Mono requestResponse(Payload payload) { + return Mono.from(s -> { + Tuple initialPayload = new Tuple<>(ByteBufferUtil.toUtf8String(payload.getData()), + ByteBufferUtil.toUtf8String(payload.getMetadata())); + String marble = requestResponseMarbles.get(initialPayload); + consoleUtils.initialPayload("Received requestresponse " + initialPayload.getK() + + " " + initialPayload.getV()); + if (marble != null) { + ParseMarble pm = new ParseMarble(marble, s, "[SERVER]"); + s.onSubscribe(new TestSubscription(pm)); + new ParseThread(pm).start(); + } else { + consoleUtils.failure("Request response payload " + initialPayload.getK() + " " + initialPayload.getV() + + "has no handler"); + } + }); + } + + @Override + public Flux requestStream(Payload payload) { + return Flux.from(s -> { + Tuple initialPayload = new Tuple<>(ByteBufferUtil.toUtf8String(payload.getData()), + ByteBufferUtil.toUtf8String(payload.getMetadata())); + String marble = requestStreamMarbles.get(initialPayload); + consoleUtils.initialPayload("Received Stream " + initialPayload.getK() + " " + initialPayload.getV()); + if (marble != null) { + ParseMarble pm = new ParseMarble(marble, s, "[SERVER]"); + s.onSubscribe(new TestSubscription(pm)); + new ParseThread(pm).start(); + } else { + consoleUtils.failure("Request stream payload " + initialPayload.getK() + " " + initialPayload.getV() + + "has no handler"); + } + }); + } + }); + } + } + + + /** + * This function parses through each line of the server handlers and primes the supporting data structures to + * be prepared for the first request. We return a RequestHandler object, which tells the ReactiveSocket server + * how to handle each type of request. The code inside the RequestHandler is lazily evaluated, and only does so + * before the first request. This may lead to a sort of bug, where getting concurrent requests as an initial request + * will nondeterministically lead to some data structures to not be initialized. + * @return a RequestHandler that details how to handle each type of request. + */ + public void parse() { + try { + String line = reader.readLine(); + while (line != null) { + String[] args = line.split("%%"); + switch (args[0]) { + case "rr": + // put the request response marble in the hash table + requestResponseMarbles.put(new Tuple<>(args[1], args[2]), args[3]); + break; + case "rs": + requestStreamMarbles.put(new Tuple<>(args[1], args[2]), args[3]); + break; + case "sub": + requestSubscriptionMarbles.put(new Tuple<>(args[1], args[2]), args[3]); + break; + case "channel": + handleChannel(args, reader); + case "echochannel": + requestEchoChannel.add(new Tuple<>(args[1], args[2])); + break; + default: + break; + } + + line = reader.readLine(); + } + + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * This handles the creation of a channel handler, it basically groups together all the lines of the channel + * script and put it in a map for later access + * @param args + * @param reader + * @throws IOException + */ + private void handleChannel(String[] args, BufferedReader reader) throws IOException { + Tuple initialPayload = new Tuple<>(args[1], args[2]); + if (args.length == 5) { + // we know that this test should fail + requestChannelFail.add(initialPayload); + } + String line = reader.readLine(); + List commands = new ArrayList<>(); + while (!line.equals("}")) { + commands.add(line); + line = reader.readLine(); + } + requestChannelCommands.put(initialPayload, commands); + } + + /** + * A trivial subscription used to interface with the ParseMarble object + */ + private class TestSubscription implements Subscription { + private ParseMarble pm; + public TestSubscription(ParseMarble pm) { + this.pm = pm; + } + + @Override + public void cancel() { + pm.cancel(); + } + + @Override + public void request(long n) { + consoleUtils.info("TestSubscription: request received for " + n); + pm.request(n); + } + } + +} diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaTCPServer.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaTCPServer.java new file mode 100644 index 000000000..4367dd11e --- /dev/null +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaTCPServer.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016 Facebook, Inc. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.reactivesocket.tckdrivers.server; + +import io.reactivesocket.transport.netty.server.TcpTransportServer; + +import reactor.ipc.netty.tcp.TcpServer; + +import java.util.concurrent.CountDownLatch; + +/** + * An example of how to run the JavaServerDriver using the ReactiveSocket server creation tool in Java. + */ +public class JavaTCPServer { + + private CountDownLatch mutex; + + public JavaTCPServer() { + mutex = new CountDownLatch(1); + } + + public void run(String realfile, int port) { + + String file = "reactivesocket-tck-drivers/src/main/resources/servertest$.txt"; + + if (realfile != null) { + file = realfile; + } + + TcpTransportServer server = TcpTransportServer.create(TcpServer.create(port)); + + JavaServerDriver jsd = + new JavaServerDriver(file, server, mutex); + jsd.run(); + } + + /** + * Blocks until the server has started + */ + public void awaitStart() { + try { + mutex.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/reactivesocket-tck-drivers/src/test/resources/clienttest$.txt b/reactivesocket-tck-drivers/src/test/resources/clienttest$.txt new file mode 100644 index 000000000..c260bfd1b --- /dev/null +++ b/reactivesocket-tck-drivers/src/test/resources/clienttest$.txt @@ -0,0 +1,73 @@ +! +name%%streamTestFail +fail +subscribe%%rs%%8c6936c1-ef13-4087-b5ce-4198f05401da%%c%%d +request%%2%%8c6936c1-ef13-4087-b5ce-4198f05401da +await%%no_events%%8c6936c1-ef13-4087-b5ce-4198f05401da%%5000 +await%%atLeast%%8c6936c1-ef13-4087-b5ce-4198f05401da%%2%%100 +cancel%%8c6936c1-ef13-4087-b5ce-4198f05401da +assert%%canceled%%8c6936c1-ef13-4087-b5ce-4198f05401da +assert%%no_error%%8c6936c1-ef13-4087-b5ce-4198f05401da +! +name%%streamTest2 +pass +subscribe%%rs%%e28023e4-995f-42f1-a70d-0d9939f3cb8f%%c%%d +request%%2%%e28023e4-995f-42f1-a70d-0d9939f3cb8f +await%%atLeast%%e28023e4-995f-42f1-a70d-0d9939f3cb8f%%2%%100 +cancel%%e28023e4-995f-42f1-a70d-0d9939f3cb8f +assert%%canceled%%e28023e4-995f-42f1-a70d-0d9939f3cb8f +assert%%no_error%%e28023e4-995f-42f1-a70d-0d9939f3cb8f +! +name%%streamTest +pass +subscribe%%rs%%d71695da-daa1-439b-97c5-25f92dd179a8%%a%%b +request%%1%%d71695da-daa1-439b-97c5-25f92dd179a8 +await%%atLeast%%d71695da-daa1-439b-97c5-25f92dd179a8%%3%%100 +assert%%received%%d71695da-daa1-439b-97c5-25f92dd179a8%%a,b&&c,d&&e,f +request%%3%%d71695da-daa1-439b-97c5-25f92dd179a8 +await%%terminal%%d71695da-daa1-439b-97c5-25f92dd179a8 +assert%%completed%%d71695da-daa1-439b-97c5-25f92dd179a8 +assert%%no_error%%d71695da-daa1-439b-97c5-25f92dd179a8 +assert%%received_at_least%%d71695da-daa1-439b-97c5-25f92dd179a8%%6 +! +name%%requestresponsePass2 +pass +subscribe%%rr%%3ae2dbb1-e6ab-4326-8da0-6fd1c9754d33%%e%%f +request%%1%%3ae2dbb1-e6ab-4326-8da0-6fd1c9754d33 +await%%terminal%%3ae2dbb1-e6ab-4326-8da0-6fd1c9754d33 +assert%%error%%3ae2dbb1-e6ab-4326-8da0-6fd1c9754d33 +assert%%no_completed%%3ae2dbb1-e6ab-4326-8da0-6fd1c9754d33 +! +name%%requestresponseFail +fail +subscribe%%rr%%6b42627e-4646-4f80-b0df-badb92429d6c%%c%%d +request%%1%%6b42627e-4646-4f80-b0df-badb92429d6c +await%%terminal%%6b42627e-4646-4f80-b0df-badb92429d6c +assert%%received%%6b42627e-4646-4f80-b0df-badb92429d6c%%ding,dong +assert%%completed%%6b42627e-4646-4f80-b0df-badb92429d6c +assert%%no_completed%%6b42627e-4646-4f80-b0df-badb92429d6c +assert%%no_error%%6b42627e-4646-4f80-b0df-badb92429d6c +! +name%%requestresponsePass +pass +subscribe%%rr%%0156b516-69ea-4273-b46a-6b1e71f9e82e%%a%%b +request%%1%%0156b516-69ea-4273-b46a-6b1e71f9e82e +await%%terminal%%0156b516-69ea-4273-b46a-6b1e71f9e82e +assert%%completed%%0156b516-69ea-4273-b46a-6b1e71f9e82e +! +name%%fireAndForget2 +pass +subscribe%%fnf%%8cf0c5f8-78ff-4980-958c-d4be0a1369e2%%c%%d +request%%1%%8cf0c5f8-78ff-4980-958c-d4be0a1369e2 +await%%terminal%%8cf0c5f8-78ff-4980-958c-d4be0a1369e2 +assert%%no_error%%8cf0c5f8-78ff-4980-958c-d4be0a1369e2 +assert%%completed%%8cf0c5f8-78ff-4980-958c-d4be0a1369e2 +! +name%%fireAndForget +pass +subscribe%%fnf%%8e1a9524-2678-4043-8f30-e66acb0bf9b6%%a%%b +request%%1%%8e1a9524-2678-4043-8f30-e66acb0bf9b6 +await%%terminal%%8e1a9524-2678-4043-8f30-e66acb0bf9b6 +assert%%no_error%%8e1a9524-2678-4043-8f30-e66acb0bf9b6 +assert%%completed%%8e1a9524-2678-4043-8f30-e66acb0bf9b6 +EOF diff --git a/reactivesocket-tck-drivers/src/test/resources/servertest$.txt b/reactivesocket-tck-drivers/src/test/resources/servertest$.txt new file mode 100644 index 000000000..8e22bca54 --- /dev/null +++ b/reactivesocket-tck-drivers/src/test/resources/servertest$.txt @@ -0,0 +1,6 @@ +rs%%a%%b%%---a-----b-----c-----d--e--f---|&&{"a":{"a":"b"},"b":{"c":"d"},"c":{"e":"f"}} +rs%%c%%d%%---a-----b-----c-----d--e--f---|&&{"a":{"a":"b"},"b":{"c":"d"},"c":{"e":"f"}} +rr%%a%%b%%------------x------------------------|&&{"x":{"hello":"goodbye"}} +rr%%c%%d%%--------------------------------x--------------------------------|&&{"x":{"ding":"dong"}} +rr%%e%%f%%------------------------------------------# +rr%%g%%h%%- \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 811b91389..d58e0e6bb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,4 +24,5 @@ include 'reactivesocket-spectator' include 'reactivesocket-test' include 'reactivesocket-transport-aeron' include 'reactivesocket-transport-local' -include 'reactivesocket-transport-netty' \ No newline at end of file +include 'reactivesocket-transport-netty' +include 'reactivesocket-tck-drivers' From c2307d9ea96f8ff4fc9330a77a8c22cc438979b5 Mon Sep 17 00:00:00 2001 From: Ryland Degnan Date: Wed, 8 Mar 2017 10:35:55 -0800 Subject: [PATCH 017/731] Added websocket transport (#255) --- .../netty/NettyDuplexConnection.java | 1 - .../netty/WebsocketDuplexConnection.java | 85 +++++++++++++++++++ .../client/WebsocketTransportClient.java | 53 ++++++++++++ .../server/WebsocketTransportServer.java | 84 ++++++++++++++++++ ...rverTest.java => TcpClientServerTest.java} | 2 +- .../transport/netty/TcpClientSetupRule.java | 4 +- .../transport/netty/TcpPing.java | 3 +- .../netty/WebsocketClientServerTest.java | 54 ++++++++++++ .../netty/WebsocketClientSetupRule.java | 42 +++++++++ .../transport/netty/WebsocketPing.java | 44 ++++++++++ .../transport/netty/WebsocketPongServer.java | 30 +++++++ 11 files changed, 396 insertions(+), 6 deletions(-) create mode 100644 reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/WebsocketDuplexConnection.java create mode 100644 reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/client/WebsocketTransportClient.java create mode 100644 reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/server/WebsocketTransportServer.java rename reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/{ClientServerTest.java => TcpClientServerTest.java} (97%) create mode 100644 reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientServerTest.java create mode 100644 reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientSetupRule.java create mode 100644 reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketPing.java create mode 100644 reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketPongServer.java diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java index 2a7abc3f9..4a5438d4f 100644 --- a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java @@ -36,7 +36,6 @@ public NettyDuplexConnection(NettyInbound in, NettyOutbound out, NettyContext co this.in = in; this.out = out; this.context = context; - //context.onClose(() -> close().subscribe()); } @Override diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/WebsocketDuplexConnection.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/WebsocketDuplexConnection.java new file mode 100644 index 000000000..c19335a61 --- /dev/null +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/WebsocketDuplexConnection.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.transport.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; +import io.reactivesocket.DuplexConnection; +import io.reactivesocket.Frame; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.NettyContext; +import reactor.ipc.netty.NettyInbound; +import reactor.ipc.netty.NettyOutbound; + +import java.nio.ByteBuffer; + +public class WebsocketDuplexConnection implements DuplexConnection { + private final NettyInbound in; + private final NettyOutbound out; + private final NettyContext context; + + public WebsocketDuplexConnection(NettyInbound in, NettyOutbound out, NettyContext context) { + this.in = in; + this.out = out; + this.context = context; + } + + @Override + public Mono send(Publisher frames) { + return Flux.from(frames) + .concatMap(this::sendOne) + .then(); + } + + @Override + public Mono sendOne(Frame frame) { + ByteBuffer src = frame.getByteBuffer(); + ByteBuf msg = out.alloc().buffer(src.remaining()).writeBytes(src); + return out.sendObject(new BinaryWebSocketFrame(msg)).then(); + } + + @Override + public Flux receive() { + return in + .receive() + .map(byteBuf -> { + ByteBuffer buffer = ByteBuffer.allocate(byteBuf.capacity()); + byteBuf.getBytes(0, buffer); + return Frame.from(buffer); + }); + } + + @Override + public Mono close() { + return Mono.fromRunnable(() -> { + if (!context.isDisposed()) { + context.channel().close(); + } + }); + } + + @Override + public Mono onClose() { + return context.onClose(); + } + + @Override + public double availability() { + return context.isDisposed() ? 0.0 : 1.0; + } +} \ No newline at end of file diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/client/WebsocketTransportClient.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/client/WebsocketTransportClient.java new file mode 100644 index 000000000..8ef7f4f79 --- /dev/null +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/client/WebsocketTransportClient.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivesocket.transport.netty.client; + +import io.reactivesocket.DuplexConnection; +import io.reactivesocket.transport.TransportClient; +import io.reactivesocket.transport.netty.WebsocketDuplexConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.http.client.HttpClient; + +public class WebsocketTransportClient implements TransportClient { + private final Logger logger = LoggerFactory.getLogger(WebsocketTransportClient.class); + private final HttpClient client; + + private WebsocketTransportClient(HttpClient client) { + this.client = client; + } + + public static WebsocketTransportClient create(HttpClient client) { + return new WebsocketTransportClient(client); + } + + @Override + public Mono connect() { + return Mono.create(sink -> + client.ws("/").then(response -> + response.receiveWebsocket((in, out) -> { + WebsocketDuplexConnection connection = new WebsocketDuplexConnection(in, out, in.context()); + sink.success(connection); + return connection.onClose(); + }) + ) + .doOnError(sink::error) + .subscribe() + ); + } +} diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/server/WebsocketTransportServer.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/server/WebsocketTransportServer.java new file mode 100644 index 000000000..954f91908 --- /dev/null +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/server/WebsocketTransportServer.java @@ -0,0 +1,84 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivesocket.transport.netty.server; + +import io.reactivesocket.transport.TransportServer; +import io.reactivesocket.transport.netty.WebsocketDuplexConnection; +import reactor.ipc.netty.NettyContext; +import reactor.ipc.netty.http.server.HttpServer; + +import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; + +public class WebsocketTransportServer implements TransportServer { + HttpServer server; + + public WebsocketTransportServer(HttpServer server) { + this.server = server; + } + + public static WebsocketTransportServer create(HttpServer server) { + return new WebsocketTransportServer(server); + } + + @Override + public StartedServer start(TransportServer.ConnectionAcceptor acceptor) { + NettyContext context = server.newHandler((request, response) -> + response.sendWebsocket((in, out) -> { + WebsocketDuplexConnection connection = new WebsocketDuplexConnection(in, out, in.context()); + acceptor.apply(connection).subscribe(); + + return out.neverComplete(); + }) + ).block(); + + return new StartServerImpl(context); + } + + static class StartServerImpl implements StartedServer { + NettyContext context; + + StartServerImpl(NettyContext context) { + this.context = context; + } + + @Override + public InetSocketAddress getServerAddress() { + return context.address(); + } + + @Override + public int getServerPort() { + return context.address().getPort(); + } + + @Override + public void awaitShutdown() { + context.onClose().block(); + } + + @Override + public void awaitShutdown(long duration, TimeUnit durationUnit) { + context.onClose().blockMillis(TimeUnit.MILLISECONDS.convert(duration, durationUnit)); + } + + @Override + public void shutdown() { + context.dispose(); + } + } +} diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/ClientServerTest.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java similarity index 97% rename from reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/ClientServerTest.java rename to reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java index 7ff0805d0..41d0cc1ab 100644 --- a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/ClientServerTest.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java @@ -20,7 +20,7 @@ import org.junit.Rule; import org.junit.Test; -public class ClientServerTest { +public class TcpClientServerTest { @Rule public final ClientSetupRule setup = new TcpClientSetupRule(); diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientSetupRule.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientSetupRule.java index a2634e011..79fc9efca 100644 --- a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientSetupRule.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientSetupRule.java @@ -30,8 +30,8 @@ public class TcpClientSetupRule extends ClientSetupRule { public TcpClientSetupRule() { - super(address -> TcpTransportClient.create(TcpClient.create(options -> options.connect((InetSocketAddress) address))), () -> { - return ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) + super(address -> TcpTransportClient.create(TcpClient.create(((InetSocketAddress)address).getPort())), () -> { + return ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create(0))) .start((setup, sendingSocket) -> { return new DisabledLeaseAcceptingSocket(new TestReactiveSocket()); }) diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPing.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPing.java index 429005841..c0698123d 100644 --- a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPing.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPing.java @@ -23,7 +23,6 @@ import org.HdrHistogram.Recorder; import reactor.ipc.netty.tcp.TcpClient; -import java.net.InetSocketAddress; import java.time.Duration; public final class TcpPing { @@ -31,7 +30,7 @@ public final class TcpPing { public static void main(String... args) throws Exception { SetupProvider setup = SetupProvider.keepAlive(KeepAliveProvider.never()).disableLease(); ReactiveSocketClient client = - ReactiveSocketClient.create(TcpTransportClient.create(TcpClient.create(options -> options.connect(new InetSocketAddress("localhost", 7878)))), setup); + ReactiveSocketClient.create(TcpTransportClient.create(TcpClient.create(7878)), setup); PingClient pingClient = new PingClient(client); Recorder recorder = pingClient.startTracker(Duration.ofSeconds(1)); final int count = 1_000_000_000; diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientServerTest.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientServerTest.java new file mode 100644 index 000000000..f3170adfb --- /dev/null +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientServerTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.transport.netty; + +import io.reactivesocket.test.ClientSetupRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; + +public class WebsocketClientServerTest { + + @Rule + public final ClientSetupRule setup = new WebsocketClientSetupRule(); + + @Test(timeout = 10000) + public void testRequestResponse1() { + setup.testRequestResponseN(1); + } + + @Test(timeout = 10000) + public void testRequestResponse10() { + setup.testRequestResponseN(10); + } + + + @Test(timeout = 10000) + public void testRequestResponse100() { + setup.testRequestResponseN(100); + } + + @Test(timeout = 10000) + public void testRequestResponse10_000() { + setup.testRequestResponseN(10_000); + } + + @Ignore("Fix request-stream") + @Test(timeout = 10000) + public void testRequestStream() { + setup.testRequestStream(); + } +} \ No newline at end of file diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientSetupRule.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientSetupRule.java new file mode 100644 index 000000000..4135c5924 --- /dev/null +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientSetupRule.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivesocket.transport.netty; + +import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; +import io.reactivesocket.server.ReactiveSocketServer; +import io.reactivesocket.test.ClientSetupRule; +import io.reactivesocket.test.TestReactiveSocket; +import io.reactivesocket.transport.netty.client.WebsocketTransportClient; +import io.reactivesocket.transport.netty.server.WebsocketTransportServer; +import reactor.ipc.netty.http.client.HttpClient; +import reactor.ipc.netty.http.server.HttpServer; + +import java.net.InetSocketAddress; + +public class WebsocketClientSetupRule extends ClientSetupRule { + + public WebsocketClientSetupRule() { + super(address -> WebsocketTransportClient.create(HttpClient.create(((InetSocketAddress)address).getPort())), () -> { + return ReactiveSocketServer.create(WebsocketTransportServer.create(HttpServer.create(0))) + .start((setup, sendingSocket) -> { + return new DisabledLeaseAcceptingSocket(new TestReactiveSocket()); + }) + .getServerAddress(); + }); + } + +} diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketPing.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketPing.java new file mode 100644 index 000000000..9f8d53c81 --- /dev/null +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketPing.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.transport.netty; + +import io.reactivesocket.client.KeepAliveProvider; +import io.reactivesocket.client.ReactiveSocketClient; +import io.reactivesocket.client.SetupProvider; +import io.reactivesocket.test.PingClient; +import io.reactivesocket.transport.netty.client.WebsocketTransportClient; +import org.HdrHistogram.Recorder; +import reactor.ipc.netty.http.client.HttpClient; + +import java.time.Duration; + +public final class WebsocketPing { + + public static void main(String... args) throws Exception { + SetupProvider setup = SetupProvider.keepAlive(KeepAliveProvider.never()).disableLease(); + ReactiveSocketClient client = + ReactiveSocketClient.create(WebsocketTransportClient.create(HttpClient.create(7878)), setup); + PingClient pingClient = new PingClient(client); + Recorder recorder = pingClient.startTracker(Duration.ofSeconds(1)); + final int count = 1_000_000_000; + pingClient.connect() + .startPingPong(count, recorder) + .doOnTerminate(() -> { + System.out.println("Sent " + count + " messages."); + }) + .blockLast(); + } +} diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketPongServer.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketPongServer.java new file mode 100644 index 000000000..6f6bf1093 --- /dev/null +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketPongServer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.transport.netty; + +import io.reactivesocket.server.ReactiveSocketServer; +import io.reactivesocket.test.PingHandler; +import io.reactivesocket.transport.netty.server.WebsocketTransportServer; +import reactor.ipc.netty.http.server.HttpServer; + +public final class WebsocketPongServer { + + public static void main(String... args) throws Exception { + ReactiveSocketServer.create(WebsocketTransportServer.create(HttpServer.create(7878))) + .start(new PingHandler()) + .awaitShutdown(); + } +} From c9d4ee60edba4b17402f9660626c5d6140401813 Mon Sep 17 00:00:00 2001 From: Robert Roeser Date: Wed, 8 Mar 2017 11:23:16 -0800 Subject: [PATCH 018/731] added logging to the ClientServerInputMultiplexer When the logger io.reactivesocket.FrameLogger is set to debug frames will be logged --- README.md | 3 +++ .../ClientServerInputMultiplexer.java | 27 ++++++++++++++++--- .../src/test/resources/log4j.properties | 3 ++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ba17a3bdf..0abbf934b 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,9 @@ dependencies { No releases to Maven Central or JCenter have occurred yet. +## Debugging +Frames can be printed out to help debugging. Set the logger `io.reactivesocket.FrameLogger` to debug to print the frames. + ## Bugs and Feedback For bugs, questions and discussions please use the [Github Issues](https://github.com/ReactiveSocket/reactivesocket-java/issues). diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java index cf6e5ea6e..30980f71e 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java @@ -20,6 +20,8 @@ import io.reactivesocket.Frame; import org.agrona.BitUtil; import org.reactivestreams.Publisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoProcessor; @@ -35,12 +37,13 @@

  • Frames for streams initiated by the acceptor of the connection (server).
  • */ public class ClientServerInputMultiplexer { + private static final Logger LOGGER = LoggerFactory.getLogger("io.reactivesocket.FrameLogger"); private final InternalDuplexConnection streamZeroConnection; private final InternalDuplexConnection serverConnection; private final InternalDuplexConnection clientConnection; - private enum Type { ZERO, CLIENT, SERVER } + private enum Type { STREAM_ZERO, CLIENT, SERVER } public ClientServerInputMultiplexer(DuplexConnection source) { final MonoProcessor> streamZero = MonoProcessor.create(); @@ -56,7 +59,7 @@ public ClientServerInputMultiplexer(DuplexConnection source) { int streamId = frame.getStreamId(); Type type; if (streamId == 0) { - type = Type.ZERO; + type = Type.STREAM_ZERO; } else if (BitUtil.isEven(streamId)) { type = Type.SERVER; } else { @@ -66,7 +69,7 @@ public ClientServerInputMultiplexer(DuplexConnection source) { }) .subscribe(group -> { switch (group.key()) { - case ZERO: + case STREAM_ZERO: streamZero.onNext(group); break; case SERVER: @@ -94,25 +97,41 @@ public DuplexConnection asStreamZeroConnection() { private static class InternalDuplexConnection implements DuplexConnection { private final DuplexConnection source; private final MonoProcessor> processor; + private final boolean debugEnabled; public InternalDuplexConnection(DuplexConnection source, MonoProcessor> processor) { this.source = source; this.processor = processor; + this.debugEnabled = LOGGER.isDebugEnabled(); } @Override public Mono send(Publisher frame) { + if (debugEnabled) { + frame = Flux.from(frame).doOnNext(f -> LOGGER.debug(f.toString())); + } + return source.send(frame); } @Override public Mono sendOne(Frame frame) { + if (debugEnabled) { + LOGGER.debug(frame.toString()); + } + return source.sendOne(frame); } @Override public Flux receive() { - return processor.flatMap(f -> f); + return processor.flatMap(f -> { + if (debugEnabled) { + return f.doOnNext(frame -> LOGGER.debug(frame.toString())); + } else { + return f; + } + }); } @Override diff --git a/reactivesocket-transport-netty/src/test/resources/log4j.properties b/reactivesocket-transport-netty/src/test/resources/log4j.properties index e1edb1274..1d1a171a5 100644 --- a/reactivesocket-transport-netty/src/test/resources/log4j.properties +++ b/reactivesocket-transport-netty/src/test/resources/log4j.properties @@ -14,4 +14,5 @@ log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} %5p [%t] (%F:%L) - %m%n \ No newline at end of file +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %5p [%t] (%F) - %m%n +#log4j.logger.io.reactivesocket.FrameLogger=Debug \ No newline at end of file From 53e931b84ffd203452be414f89d0f893a43344fd Mon Sep 17 00:00:00 2001 From: Robert Roeser Date: Thu, 9 Mar 2017 00:09:24 -0800 Subject: [PATCH 019/731] added logging to the ClientServerInputMultiplexer (#256) When the logger io.reactivesocket.FrameLogger is set to debug frames will be logged --- README.md | 3 +++ .../ClientServerInputMultiplexer.java | 27 ++++++++++++++++--- .../src/test/resources/log4j.properties | 3 ++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ba17a3bdf..0abbf934b 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,9 @@ dependencies { No releases to Maven Central or JCenter have occurred yet. +## Debugging +Frames can be printed out to help debugging. Set the logger `io.reactivesocket.FrameLogger` to debug to print the frames. + ## Bugs and Feedback For bugs, questions and discussions please use the [Github Issues](https://github.com/ReactiveSocket/reactivesocket-java/issues). diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java index cf6e5ea6e..30980f71e 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java @@ -20,6 +20,8 @@ import io.reactivesocket.Frame; import org.agrona.BitUtil; import org.reactivestreams.Publisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoProcessor; @@ -35,12 +37,13 @@
  • Frames for streams initiated by the acceptor of the connection (server).
  • */ public class ClientServerInputMultiplexer { + private static final Logger LOGGER = LoggerFactory.getLogger("io.reactivesocket.FrameLogger"); private final InternalDuplexConnection streamZeroConnection; private final InternalDuplexConnection serverConnection; private final InternalDuplexConnection clientConnection; - private enum Type { ZERO, CLIENT, SERVER } + private enum Type { STREAM_ZERO, CLIENT, SERVER } public ClientServerInputMultiplexer(DuplexConnection source) { final MonoProcessor> streamZero = MonoProcessor.create(); @@ -56,7 +59,7 @@ public ClientServerInputMultiplexer(DuplexConnection source) { int streamId = frame.getStreamId(); Type type; if (streamId == 0) { - type = Type.ZERO; + type = Type.STREAM_ZERO; } else if (BitUtil.isEven(streamId)) { type = Type.SERVER; } else { @@ -66,7 +69,7 @@ public ClientServerInputMultiplexer(DuplexConnection source) { }) .subscribe(group -> { switch (group.key()) { - case ZERO: + case STREAM_ZERO: streamZero.onNext(group); break; case SERVER: @@ -94,25 +97,41 @@ public DuplexConnection asStreamZeroConnection() { private static class InternalDuplexConnection implements DuplexConnection { private final DuplexConnection source; private final MonoProcessor> processor; + private final boolean debugEnabled; public InternalDuplexConnection(DuplexConnection source, MonoProcessor> processor) { this.source = source; this.processor = processor; + this.debugEnabled = LOGGER.isDebugEnabled(); } @Override public Mono send(Publisher frame) { + if (debugEnabled) { + frame = Flux.from(frame).doOnNext(f -> LOGGER.debug(f.toString())); + } + return source.send(frame); } @Override public Mono sendOne(Frame frame) { + if (debugEnabled) { + LOGGER.debug(frame.toString()); + } + return source.sendOne(frame); } @Override public Flux receive() { - return processor.flatMap(f -> f); + return processor.flatMap(f -> { + if (debugEnabled) { + return f.doOnNext(frame -> LOGGER.debug(frame.toString())); + } else { + return f; + } + }); } @Override diff --git a/reactivesocket-transport-netty/src/test/resources/log4j.properties b/reactivesocket-transport-netty/src/test/resources/log4j.properties index e1edb1274..1d1a171a5 100644 --- a/reactivesocket-transport-netty/src/test/resources/log4j.properties +++ b/reactivesocket-transport-netty/src/test/resources/log4j.properties @@ -14,4 +14,5 @@ log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} %5p [%t] (%F:%L) - %m%n \ No newline at end of file +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %5p [%t] (%F) - %m%n +#log4j.logger.io.reactivesocket.FrameLogger=Debug \ No newline at end of file From 1a3e07a51b6005e514a8dd2fc3cca0fd1ff38a22 Mon Sep 17 00:00:00 2001 From: Robert Roeser Date: Thu, 9 Mar 2017 16:00:39 -0800 Subject: [PATCH 020/731] refactoring client to use reactor-core --- .../reactivesocket/ClientReactiveSocket.java | 248 +++++++++--------- .../reactivesocket/ReactiveSocketFactory.java | 40 --- .../reactivesocket/ServerReactiveSocket.java | 13 +- .../reactivesocket/events/EventListener.java | 21 +- .../reactivesocket/frame/ByteBufferUtil.java | 4 +- .../ClientServerInputMultiplexer.java | 6 +- .../internal/LimitableRequestPublisher.java | 149 +++++++++++ .../io/reactivesocket/test/PingClient.java | 2 +- .../test/java/com/rolandkuhn/rs/Client.java | 86 ++++++ .../test/java/com/rolandkuhn/rs/Server.java | 50 ++++ 10 files changed, 431 insertions(+), 188 deletions(-) delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocketFactory.java create mode 100755 reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java create mode 100644 reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client.java create mode 100644 reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Server.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java index 09c669ff0..0f554f23a 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java @@ -18,28 +18,26 @@ import io.reactivesocket.client.KeepAliveProvider; import io.reactivesocket.events.EventListener; -import io.reactivesocket.events.EventPublishingSocket; -import io.reactivesocket.events.EventPublishingSocketImpl; -import io.reactivesocket.exceptions.CancelException; import io.reactivesocket.exceptions.Exceptions; -import io.reactivesocket.internal.*; +import io.reactivesocket.internal.DisabledEventPublisher; +import io.reactivesocket.internal.EventPublisher; +import io.reactivesocket.internal.KnownErrorFilter; +import io.reactivesocket.internal.LimitableRequestPublisher; import io.reactivesocket.lease.Lease; import io.reactivesocket.lease.LeaseImpl; import org.agrona.collections.Int2ObjectHashMap; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; import reactor.core.Disposable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.core.publisher.MonoSource; +import reactor.core.publisher.MonoProcessor; +import reactor.core.publisher.UnicastProcessor; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; -import static io.reactivesocket.events.EventListener.RequestType.*; - /** * Client Side of a ReactiveSocket socket. Sends {@link Frame}s * to a {@link ServerReactiveSocket} @@ -50,12 +48,10 @@ public class ClientReactiveSocket implements ReactiveSocket { private final Consumer errorConsumer; private final StreamIdSupplier streamIdSupplier; private final KeepAliveProvider keepAliveProvider; - private final EventPublishingSocket eventPublishingSocket; - - private final Int2ObjectHashMap senders; + private final MonoProcessor started; + private final Int2ObjectHashMap senders; private final Int2ObjectHashMap> receivers; - private final BufferingSubscription transportReceiveSubscription = new BufferingSubscription(); private Disposable keepAliveSendSub; private volatile Consumer leaseConsumer; // Provided on start() @@ -66,8 +62,8 @@ public ClientReactiveSocket(DuplexConnection connection, Consumer err this.errorConsumer = new KnownErrorFilter(errorConsumer); this.streamIdSupplier = streamIdSupplier; this.keepAliveProvider = keepAliveProvider; - eventPublishingSocket = publisher.isEventPublishingEnabled()? new EventPublishingSocketImpl(publisher, true) - : EventPublishingSocket.DISABLED; + this.started = MonoProcessor.create(); + senders = new Int2ObjectHashMap<>(256, 0.9f); receivers = new Int2ObjectHashMap<>(256, 0.9f); connection.onClose() @@ -82,11 +78,13 @@ public ClientReactiveSocket(DuplexConnection connection, Consumer err @Override public Mono fireAndForget(Payload payload) { - return Mono.defer(() -> { + Mono defer = Mono.defer(() -> { final int streamId = nextStreamId(); final Frame requestFrame = Frame.Request.from(streamId, FrameType.FIRE_AND_FORGET, payload, 0); return connection.sendOne(requestFrame); }); + + return started.then(defer); } @Override @@ -127,62 +125,129 @@ public Mono onClose() { public ClientReactiveSocket start(Consumer leaseConsumer) { this.leaseConsumer = leaseConsumer; - startKeepAlive(); - startReceivingRequests(); + + keepAliveSendSub = connection.send(keepAliveProvider.ticks() + .map(i -> Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, true))) + .subscribe(null, errorConsumer); + + connection + .receive() + .doOnSubscribe(subscription -> started.onComplete()) + .doOnNext(this::handleIncomingFrames) + .doOnError(errorConsumer) + .subscribe(); + return this; } private Mono handleRequestResponse(final Payload payload) { - return MonoSource.wrap(subscriber -> { + return started.then(() -> { int streamId = nextStreamId(); final Frame requestFrame = Frame.Request.from(streamId, FrameType.REQUEST_RESPONSE, payload, 1); + + MonoProcessor receiver = MonoProcessor.create(); + synchronized (this) { - receivers.put(streamId, subscriber); + receivers.put(streamId, receiver); } - Mono send = eventPublishingSocket.decorateSend(streamId, connection.sendOne(requestFrame), 0, - RequestResponse); - eventPublishingSocket.decorateReceive(streamId, send.then(Mono.never()) + + MonoProcessor subscribedRequest = connection + .sendOne(requestFrame) + .doOnError(t -> { + errorConsumer.accept(t); + receiver.cancel(); + }) + .subscribe(); + + return receiver + .doOnError(t -> { + if (contains(streamId) && connection.availability() > 0.0) { + connection + .sendOne(Frame.Error.from(streamId, t)) + .doOnError(errorConsumer::accept) + .subscribe(); + } + }) .doOnCancel(() -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.Cancel.from(streamId)).subscribe(); + if (contains(streamId) && connection.availability() > 0.0) { + connection + .sendOne(Frame.Cancel.from(streamId)) + .doOnError(errorConsumer::accept) + .subscribe(); } - removeReceiver(streamId); - }), RequestResponse).subscribe(subscriber); + subscribedRequest.cancel(); + }) + .doFinally(s -> + removeReceiver(streamId) + ); }); } private Flux handleStreamResponse(Flux request, FrameType requestType) { - return Flux.defer(() -> { + return started.thenMany(() -> { int streamId = nextStreamId(); - RemoteSender sender = new RemoteSender(request.map(payload -> Frame.Request.from(streamId, requestType, - payload, 1)), - removeSenderLambda(streamId), streamId, 1); - Publisher src = s -> { - Disposable disposable = eventPublishingSocket.decorateSend(streamId, connection.send(sender), 0, fromFrameType(requestType)) - .subscribe(null, s::onError); - ValidatingSubscription sub = ValidatingSubscription.create(s, disposable::dispose, transportReceiveSubscription::request); - s.onSubscribe(sub); - }; - - RemoteReceiver receiver = new RemoteReceiver(src, connection, streamId, removeReceiverLambda(streamId), - true); - registerSenderReceiver(streamId, sender, receiver); - return eventPublishingSocket.decorateReceive(streamId, receiver, fromFrameType(requestType)); - }); - } - private void startKeepAlive() { - keepAliveSendSub = connection.send(keepAliveProvider.ticks() - .map(i -> Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, true))) - .subscribe(null, errorConsumer); + UnicastProcessor receiver = UnicastProcessor.create(); + + Flux requestFrames = + request + .transform(f -> { + LimitableRequestPublisher wrapped = LimitableRequestPublisher.wrap(f); + synchronized (ClientReactiveSocket.this) { + senders.put(streamId, wrapped); + receivers.put(streamId, receiver); + } + + return wrapped; + }) + .doOnRequest(l -> System.out.println("request n from netty -> " + l)) + .map(payload -> Frame.Request.from(streamId, requestType, payload, 1)); + + MonoProcessor subscribedRequests = connection + .send(requestFrames) + .doOnError(t -> { + errorConsumer.accept(t); + receiver.cancel(); + }) + .subscribe(); + + return receiver + .doOnRequest(l -> { + if (contains(streamId) && connection.availability() > 0.0) { + connection + .sendOne(Frame.RequestN.from(streamId, l)) + .doOnError(receiver::onError) + .subscribe(); + } + }) + .doOnError(t -> { + if (contains(streamId) && connection.availability() > 0.0) { + connection + .sendOne(Frame.Error.from(streamId, t)) + .doOnError(errorConsumer::accept) + .subscribe(); + } + }) + .doOnCancel(() -> { + if (contains(streamId) && connection.availability() > 0.0) { + connection + .sendOne(Frame.Cancel.from(streamId)) + .doOnError(errorConsumer::accept) + .subscribe(); + } + subscribedRequests.cancel(); + }) + .doFinally(s -> { + removeReceiver(streamId); + removeSender(streamId); + }); + }); } - private void startReceivingRequests() { - connection.receive() - .doOnSubscribe(transportReceiveSubscription::switchTo) - .doOnNext(this::handleIncomingFrames) - .doOnError(errorConsumer) - .subscribe(); + private boolean contains(int streamId) { + synchronized (ClientReactiveSocket.this) { + return receivers.containsKey(streamId); + } } protected void cleanup() { @@ -190,7 +255,6 @@ protected void cleanup() { if (null != keepAliveSendSub) { keepAliveSendSub.dispose(); } - transportReceiveSubscription.cancel(); } private void handleIncomingFrames(Frame frame) { @@ -238,40 +302,34 @@ private void handleFrame(int streamId, FrameType type, Frame frame) { switch (type) { case ERROR: receiver.onError(Exceptions.from(frame)); - synchronized (this) { - receivers.remove(streamId); - } + removeReceiver(streamId); break; case NEXT_COMPLETE: receiver.onNext(frame); receiver.onComplete(); - synchronized (this) { - receivers.remove(streamId); - } break; case CANCEL: { - Subscription sender; + LimitableRequestPublisher sender; synchronized (this) { sender = senders.remove(streamId); - receivers.remove(streamId); + removeReceiver(streamId); } if (sender != null) { sender.cancel(); } - receiver.onError(new CancelException("cancelling stream id " + streamId)); break; } case NEXT: receiver.onNext(frame); break; case REQUEST_N: { - Subscription sender; + LimitableRequestPublisher sender; synchronized (this) { sender = senders.get(streamId); } if (sender != null) { int n = Frame.RequestN.requestN(frame); - sender.request(n); + sender.increaseRequestLimit(n); } break; } @@ -299,7 +357,7 @@ private void handleMissingResponseProcessor(int streamId, FrameType type, Frame + streamId + " Message: " + errorMessage); } else { throw new IllegalStateException("Client received message for non-existent stream: " + streamId + - ", frame type: " + type); + ", frame type: " + type); } } // receiving a frame after a given stream has been cancelled/completed, @@ -316,69 +374,11 @@ private static String getByteBufferAsString(ByteBuffer bb) { return new String(bytes, StandardCharsets.UTF_8); } - private Runnable removeReceiverLambda(int streamId) { - return () -> { - removeReceiver(streamId); - }; - } - private synchronized void removeReceiver(int streamId) { receivers.remove(streamId); } - private Runnable removeSenderLambda(int streamId) { - return () -> { - removeSender(streamId); - }; - } - private synchronized void removeSender(int streamId) { senders.remove(streamId); } - - private synchronized void registerSenderReceiver(int streamId, Subscription sender, Subscriber receiver) { - senders.put(streamId, sender); - receivers.put(streamId, receiver); - } - - private static class BufferingSubscription implements Subscription { - - private int requested; - private boolean cancelled; - private Subscription delegate; - - @Override - public void request(long n) { - if (relay()) { - delegate.request(n); - } else { - requested = FlowControlHelper.incrementRequestN(requested, n); - } - } - - @Override - public void cancel() { - if (relay()) { - delegate.cancel(); - } else { - cancelled = true; - } - } - - private void switchTo(Subscription subscription) { - synchronized (this) { - delegate = subscription; - } - if (requested > 0) { - subscription.request(requested); - } - if (cancelled) { - subscription.cancel(); - } - } - - private synchronized boolean relay() { - return delegate != null; - } - } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocketFactory.java b/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocketFactory.java deleted file mode 100644 index 679943275..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ReactiveSocketFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket; - -import org.reactivestreams.Publisher; -import reactor.core.publisher.Mono; - -/** - * Factory of ReactiveSocket interface - * This abstraction is useful for abstracting the creation of a ReactiveSocket - * (e.g. inside the LoadBalancer which getInstance ReactiveSocket as needed) - */ -public interface ReactiveSocketFactory { - - /** - * Construct the ReactiveSocket. - * - * @return A source that emits a single {@code ReactiveSocket}. - */ - Mono apply(); - - /** - * @return a positive numbers representing the availability of the factory. - * Higher is better, 0.0 means not available - */ - double availability(); -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index f60f5a153..6f1b44c4a 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -18,7 +18,6 @@ import io.reactivesocket.Frame.Lease; import io.reactivesocket.Frame.Request; -import io.reactivesocket.Frame.PayloadFrame; import io.reactivesocket.events.EventListener; import io.reactivesocket.events.EventListener.RequestType; import io.reactivesocket.events.EventPublishingSocket; @@ -172,7 +171,7 @@ private Mono handleFrame(Frame frame) { case REQUEST_N: return handleRequestN(streamId, frame); case REQUEST_STREAM: - return doReceive(streamId, requestStream(frame), RequestStream); + return doReceive(streamId, requestStream(frame), REQUEST_STREAM); case FIRE_AND_FORGET: return handleFireAndForget(streamId, fireAndForget(frame)); case REQUEST_CHANNEL: @@ -249,7 +248,7 @@ private synchronized void cleanup() { } private Mono handleRequestResponse(int streamId, Mono response) { - long now = publishSingleFrameReceiveEvents(streamId, RequestResponse); + long now = publishSingleFrameReceiveEvents(streamId, REQUEST_RESPONSE); Mono frames = new MonoOnErrorOrCancelReturn<>( response @@ -268,7 +267,7 @@ private Mono handleRequestResponse(int streamId, Mono response) { () -> Frame.Cancel.from(streamId) ); - return eventPublishingSocket.decorateSend(streamId, connection.send(frames), now, RequestResponse); + return eventPublishingSocket.decorateSend(streamId, connection.send(frames), now, REQUEST_RESPONSE); } private Mono doReceive(int streamId, Flux response, RequestType requestType) { @@ -280,14 +279,14 @@ private Mono doReceive(int streamId, Flux response, RequestType r } private Mono handleChannel(int streamId, Frame firstFrame) { - long now = publishSingleFrameReceiveEvents(streamId, RequestChannel); + long now = publishSingleFrameReceiveEvents(streamId, REQUEST_CHANNEL); int initialRequestN = Request.initialRequestN(firstFrame); Frame firstAsNext = Request.from(streamId, FrameType.NEXT, firstFrame, initialRequestN); RemoteReceiver receiver = new RemoteReceiver(connection, streamId, () -> removeChannelProcessor(streamId), firstAsNext, receiversSubscription, true); channelProcessors.put(streamId, receiver); - Flux response = requestChannel(eventPublishingSocket.decorateReceive(streamId, receiver, RequestChannel)) + Flux response = requestChannel(eventPublishingSocket.decorateReceive(streamId, receiver, REQUEST_CHANNEL)) .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)); RemoteSender sender = new RemoteSender(response, () -> removeSubscriptions(streamId), streamId, @@ -296,7 +295,7 @@ private Mono handleChannel(int streamId, Frame firstFrame) { subscriptions.put(streamId, sender); } - return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, RequestChannel); + return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, REQUEST_CHANNEL); } private Mono handleFireAndForget(int streamId, Mono result) { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java index 0ffc09622..6a9e1f216 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java @@ -18,7 +18,6 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.events.EventSource.EventSubscription; -import io.reactivesocket.lease.Lease; import java.util.concurrent.TimeUnit; @@ -31,24 +30,24 @@ public interface EventListener { * An enum to represent the various interaction models of {@code ReactiveSocket}. */ enum RequestType { - RequestResponse, - RequestStream, - RequestChannel, - MetadataPush, - FireAndForget; + REQUEST_RESPONSE, + REQUEST_STREAM, + REQUEST_CHANNEL, + METADATA_PUSH, + FIRE_AND_FORGET; public static RequestType fromFrameType(FrameType frameType) { switch (frameType) { case REQUEST_RESPONSE: - return RequestResponse; + return REQUEST_RESPONSE; case FIRE_AND_FORGET: - return FireAndForget; + return FIRE_AND_FORGET; case REQUEST_STREAM: - return RequestStream; + return REQUEST_STREAM; case REQUEST_CHANNEL: - return RequestChannel; + return REQUEST_CHANNEL; case METADATA_PUSH: - return MetadataPush; + return METADATA_PUSH; default: throw new IllegalArgumentException("Unknown frame type: " + frameType); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ByteBufferUtil.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ByteBufferUtil.java index 090d23ff9..d7ecc55a6 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ByteBufferUtil.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ByteBufferUtil.java @@ -24,14 +24,14 @@ public class ByteBufferUtil { private ByteBufferUtil() {} /** - * Slice a portion of the {@link ByteBuffer} while preserving the buffers position and limit. + * Slice a portion of the {@link ByteBuffer} while preserving the buffers position and LimitableRequestPublisher.java. * * NOTE: Missing functionality from {@link ByteBuffer} * * @param byteBuffer to slice off of * @param position to start slice at * @param limit to slice to - * @return slice of byteBuffer with passed ByteBuffer preserved position and limit. + * @return slice of byteBuffer with passed ByteBuffer preserved position and LimitableRequestPublisher.java. */ public static ByteBuffer preservingSlice(final ByteBuffer byteBuffer, final int position, final int limit) { final int savedPosition = byteBuffer.position(); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java index 30980f71e..e0e8fdbe1 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java @@ -108,7 +108,7 @@ public InternalDuplexConnection(DuplexConnection source, MonoProcessor send(Publisher frame) { if (debugEnabled) { - frame = Flux.from(frame).doOnNext(f -> LOGGER.debug(f.toString())); + frame = Flux.from(frame).doOnNext(f -> LOGGER.debug("sending -> " + f.toString())); } return source.send(frame); @@ -117,7 +117,7 @@ public Mono send(Publisher frame) { @Override public Mono sendOne(Frame frame) { if (debugEnabled) { - LOGGER.debug(frame.toString()); + LOGGER.debug("sending -> " + frame.toString()); } return source.sendOne(frame); @@ -127,7 +127,7 @@ public Mono sendOne(Frame frame) { public Flux receive() { return processor.flatMap(f -> { if (debugEnabled) { - return f.doOnNext(frame -> LOGGER.debug(frame.toString())); + return f.doOnNext(frame -> LOGGER.debug("receiving -> " + frame.toString())); } else { return f; } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java new file mode 100755 index 000000000..3d6c0ce82 --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java @@ -0,0 +1,149 @@ +package io.reactivesocket.internal; + +import io.reactivesocket.Payload; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import reactor.core.Fuseable; +import reactor.core.publisher.Flux; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * + */ +public class LimitableRequestPublisher extends Flux { + private final Publisher source; + + private final AtomicBoolean canceled; + + private long internalRequested; + + private long externalRequested; + + private volatile boolean subscribed; + + private volatile Subscription s; + + private LimitableRequestPublisher(Publisher source) { + this.source = source; + this.canceled = new AtomicBoolean(); + } + + public static LimitableRequestPublisher wrap(Publisher source) { + return new LimitableRequestPublisher<>(source); + } + + @Override + public void subscribe(Subscriber destination) { + synchronized (this) { + if (subscribed) { + throw new IllegalStateException("only one subscriber at a time"); + } + + subscribed = true; + } + + destination.onSubscribe(new InnerSubscription()); + source.subscribe(new InnerSubscriber<>(destination)); + + if (source instanceof Fuseable.ScalarCallable) { + Fuseable.ScalarCallable source = (Fuseable.ScalarCallable) this.source; + Object call = source.call(); + destination.onNext((T) call); + destination.onComplete(); + } + } + + + public void increaseRequestLimit(long n) { + long e; + long i; + synchronized (this) { + e = FlowControlHelper.incrementRequestN(externalRequested, n); + externalRequested = e; + i = internalRequested; + } + + requestUpstream(e, i); + } + + private void requestUpstream(long e, long i) { + long n = Math.min(e , i); + + if (n > 0 && s != null & !canceled.get()) { + s.request(n); + } + } + + public void cancel() { + if (canceled.compareAndSet(false, true) && s != null) { + s.cancel(); + s = null; + subscribed = false; + } + } + + private class InnerSubscriber implements Subscriber { + Subscriber destination; + + public InnerSubscriber(Subscriber destination) { + this.destination = destination; + } + + @Override + public void onSubscribe(Subscription s) { + synchronized (LimitableRequestPublisher.this) { + LimitableRequestPublisher.this.s = s; + + if (canceled.get()) { + s.cancel(); + subscribed = false; + LimitableRequestPublisher.this.s = null; + } + } + } + + @Override + public void onNext(T t) { + synchronized (LimitableRequestPublisher.this) { + externalRequested--; + internalRequested--; + } + + destination.onNext(t); + + } + + @Override + public void onError(Throwable t) { + destination.onError(t); + } + + @Override + public void onComplete() { + destination.onComplete(); + } + } + + private class InnerSubscription implements Subscription { + + @Override + public void request(long n) { + long e; + long i; + synchronized (this) { + i = FlowControlHelper.incrementRequestN(internalRequested, n); + internalRequested = i; + e = externalRequested; + } + + requestUpstream(e, i); + } + + @Override + public void cancel() { + LimitableRequestPublisher.this.cancel(); + } + } +} diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java index f4de1b08e..4d89b1ae2 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java @@ -66,7 +66,7 @@ public Flux startPingPong(int count, final Recorder histogram) { long diff = System.nanoTime() - start; histogram.recordValue(diff); }); - }, 16) + }) .doOnError(Throwable::printStackTrace); } } diff --git a/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client.java b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client.java new file mode 100644 index 000000000..924538b1e --- /dev/null +++ b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client.java @@ -0,0 +1,86 @@ +package com.rolandkuhn.rs; + +import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.client.KeepAliveProvider; +import io.reactivesocket.client.ReactiveSocketClient; +import io.reactivesocket.client.SetupProvider; +import io.reactivesocket.frame.ByteBufferUtil; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.util.PayloadImpl; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import reactor.ipc.netty.tcp.TcpClient; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.time.Duration; + +public class Client { + public static class Performance { + final String url; + final int count; + final double avgSize; + + public Performance(String url, int count, double avgSize) { + super(); + this.url = url; + this.count = count; + this.avgSize = avgSize; + } + + public String getUrl() { + return url; + } + + public int getCount() { + return count; + } + + public double getAvgSize() { + return avgSize; + } + + @Override + public String toString() { + return "Performance [url=" + url + ", count=" + count + ", avgSize=" + avgSize + "]"; + } + + + } + + public static Flux subscribe(ReactiveSocket socket, String request) { + return + socket + .requestStream(new PayloadImpl(request)) + .publishOn(Schedulers.single()) + .map(payload -> payload.getData()) + .map(ByteBufferUtil::toUtf8String) + .buffer(Duration.ofSeconds(1)) + .map(l -> { + double avgSize = l + .stream() + .mapToInt(String::length) + .average() + .orElse(0.0); + + return new Performance(request, l.size(), avgSize); + }); + } + + public static void main(String[] args) { + int port = 9000; + String host = "localhost"; + + SocketAddress address = new InetSocketAddress(host, port); + TcpClient client = TcpClient.create(port); + ReactiveSocket socket = Mono.from(ReactiveSocketClient.create(TcpTransportClient.create(client), + SetupProvider.keepAlive(KeepAliveProvider.never()).disableLease()).connect()).block(); + + for (int i = 0; i < 1; i++) { + subscribe(socket, "localhost:4096:Object" + i) + .doOnNext(System.out::println) + .blockLast(); + } + } +} \ No newline at end of file diff --git a/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Server.java b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Server.java new file mode 100644 index 000000000..ee9c1dd3f --- /dev/null +++ b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Server.java @@ -0,0 +1,50 @@ +package com.rolandkuhn.rs; + +import io.reactivesocket.AbstractReactiveSocket; +import io.reactivesocket.Payload; +import io.reactivesocket.frame.ByteBufferUtil; +import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; +import io.reactivesocket.server.ReactiveSocketServer; +import io.reactivesocket.transport.netty.server.TcpTransportServer; +import io.reactivesocket.util.PayloadImpl; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.tcp.TcpServer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; + +public class Server { + + public static void main(String[] args) throws IOException { + int port = 9000; + + TcpServer server = TcpServer.create(port); + ReactiveSocketServer.create(TcpTransportServer.create(server)) + .start((setupPayload, reactiveSocket) -> { + return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { + @Override + public Mono requestResponse(Payload p) { + return Mono.just(p); + } + + @Override + public Flux requestStream(Payload p) { + String[] request = ByteBufferUtil.toUtf8String(p.getData()).split(":"); + int size = Integer.parseInt(request[1]); + + System.out.println("Got request for " + request); + + final byte[] buff = new byte[size]; + Arrays.fill(buff, (byte)65); + + return Flux.generate(emitter -> emitter.next(new PayloadImpl(buff))); + } + }); + }); + + new BufferedReader(new InputStreamReader(System.in)).readLine(); + } +} \ No newline at end of file From 9668bb8d77df6d7a8154b288f4e2a39eb3fe6ada Mon Sep 17 00:00:00 2001 From: Robert Roeser Date: Fri, 10 Mar 2017 14:53:37 -0800 Subject: [PATCH 021/731] updated limitable request publisher request n --- .../reactivesocket/ServerReactiveSocket.java | 364 +++++++++--------- .../reactivesocket/ServerReactiveSocket2.java | 361 +++++++++++++++++ .../internal/LimitableRequestPublisher.java | 51 +-- .../test/java/com/rolandkuhn/rs/Client.java | 1 + 4 files changed, 570 insertions(+), 207 deletions(-) create mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket2.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index 6f1b44c4a..0246993d2 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -19,26 +19,24 @@ import io.reactivesocket.Frame.Lease; import io.reactivesocket.Frame.Request; import io.reactivesocket.events.EventListener; -import io.reactivesocket.events.EventListener.RequestType; -import io.reactivesocket.events.EventPublishingSocket; -import io.reactivesocket.events.EventPublishingSocketImpl; import io.reactivesocket.exceptions.ApplicationException; -import io.reactivesocket.exceptions.SetupException; import io.reactivesocket.frame.FrameHeaderFlyweight; -import io.reactivesocket.internal.*; +import io.reactivesocket.internal.DisabledEventPublisher; +import io.reactivesocket.internal.EventPublisher; +import io.reactivesocket.internal.KnownErrorFilter; +import io.reactivesocket.internal.LimitableRequestPublisher; import io.reactivesocket.lease.LeaseEnforcingSocket; -import io.reactivesocket.util.Clock; import org.agrona.collections.Int2ObjectHashMap; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; +import reactor.core.Disposable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.core.publisher.UnicastProcessor; import java.util.Collection; import java.util.function.Consumer; -import static io.reactivesocket.events.EventListener.RequestType.*; - /** * Server side ReactiveSocket. Receives {@link Frame}s from a * {@link ClientReactiveSocket} @@ -46,29 +44,23 @@ public class ServerReactiveSocket implements ReactiveSocket { private final DuplexConnection connection; - private final Flux serverInput; private final Consumer errorConsumer; - private final EventPublisher eventPublisher; - private final Int2ObjectHashMap subscriptions; - private final Int2ObjectHashMap channelProcessors; + private final Int2ObjectHashMap sendingSubscriptions; + private final Int2ObjectHashMap> receivers; private final ReactiveSocket requestHandler; - private Subscription receiversSubscription; - private final EventPublishingSocket eventPublishingSocket; + + private volatile Disposable subscribe; public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestHandler, - boolean clientHonorsLease, Consumer errorConsumer, - EventPublisher eventPublisher) { + boolean clientHonorsLease, Consumer errorConsumer, + EventPublisher eventPublisher) { this.requestHandler = requestHandler; this.connection = connection; - serverInput = connection.receive(); this.errorConsumer = new KnownErrorFilter(errorConsumer); - this.eventPublisher = eventPublisher; - subscriptions = new Int2ObjectHashMap<>(); - channelProcessors = new Int2ObjectHashMap<>(); - eventPublishingSocket = eventPublisher.isEventPublishingEnabled()? - new EventPublishingSocketImpl(eventPublisher, false) : EventPublishingSocket.DISABLED; + this.sendingSubscriptions = new Int2ObjectHashMap<>(); + this.receivers = new Int2ObjectHashMap<>(); connection.onClose() .doFinally(signalType -> cleanup()) @@ -81,18 +73,19 @@ public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestH } Frame leaseFrame = Lease.from(lease.getTtl(), lease.getAllowedRequests(), lease.metadata()); connection.sendOne(leaseFrame) - .doOnError(errorConsumer) - .subscribe(); + .doOnError(errorConsumer) + .subscribe(); }); } } + public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestHandler, - boolean clientHonorsLease, Consumer errorConsumer) { + boolean clientHonorsLease, Consumer errorConsumer) { this(connection, requestHandler, clientHonorsLease, errorConsumer, new DisabledEventPublisher<>()); } public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestHandler, - Consumer errorConsumer) { + Consumer errorConsumer) { this(connection, requestHandler, true, errorConsumer); } @@ -123,6 +116,10 @@ public Mono metadataPush(Payload payload) { @Override public Mono close() { + if (subscribe != null) { + subscribe.dispose(); + } + return connection.close(); } @@ -132,9 +129,75 @@ public Mono onClose() { } public ServerReactiveSocket start() { - serverInput - .doOnNext(frame -> { - handleFrame(frame).doOnError(errorConsumer).subscribe(); + subscribe = connection + .receive() + .flatMap(frame -> { + int streamId = frame.getStreamId(); + UnicastProcessor receiver; + switch (frame.getType()) { + case FIRE_AND_FORGET: + return handleFireAndForget(streamId, fireAndForget(frame)); + case REQUEST_RESPONSE: + return handleRequestResponse(streamId, requestResponse(frame)); + case CANCEL: + return handleCancelFrame(streamId); + case KEEPALIVE: + return handleKeepAliveFrame(frame); + case REQUEST_N: + return handleRequestN(streamId, frame); + case REQUEST_STREAM: + return handleStream(streamId, requestStream(frame)); + case REQUEST_CHANNEL: + return handleChannel(streamId, frame); + case PAYLOAD: + // TODO: Hook in receiving socket. + return Mono.empty(); + case METADATA_PUSH: + return metadataPush(frame); + case LEASE: + // Lease must not be received here as this is the server end of the socket which sends leases. + return Mono.empty(); + case NEXT: + synchronized (ServerReactiveSocket.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onNext(frame); + } + return Mono.empty(); + case COMPLETE: + synchronized (ServerReactiveSocket.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onComplete(); + } + return Mono.empty(); + case ERROR: + synchronized (ServerReactiveSocket.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onError(new ApplicationException(frame)); + } + return Mono.empty(); + case NEXT_COMPLETE: + synchronized (ServerReactiveSocket.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onNext(frame); + receiver.onComplete(); + } + + return Mono.empty(); + + case SETUP: + return handleError(streamId, new IllegalStateException("Setup frame received post setup.")); + default: + return handleError(streamId, new IllegalStateException("ServerReactiveSocket: Unexpected frame type: " + + frame.getType())); + } }) .doOnError(t -> { errorConsumer.accept(t); @@ -143,169 +206,113 @@ public ServerReactiveSocket start() { Collection values; synchronized (this) { - values = subscriptions.values(); + values = sendingSubscriptions.values(); } values .forEach(Subscription::cancel); }) - .doOnSubscribe(subscription -> { - receiversSubscription = subscription; - }) .subscribe(); return this; } - private Mono handleFrame(Frame frame) { - final int streamId = frame.getStreamId(); - try { - RemoteReceiver receiver; - switch (frame.getType()) { - case SETUP: - return Mono.error(new IllegalStateException("Setup frame received post setup.")); - case REQUEST_RESPONSE: - return handleRequestResponse(streamId, requestResponse(frame)); - case CANCEL: - return handleCancelFrame(streamId); - case KEEPALIVE: - return handleKeepAliveFrame(frame); - case REQUEST_N: - return handleRequestN(streamId, frame); - case REQUEST_STREAM: - return doReceive(streamId, requestStream(frame), REQUEST_STREAM); - case FIRE_AND_FORGET: - return handleFireAndForget(streamId, fireAndForget(frame)); - case REQUEST_CHANNEL: - return handleChannel(streamId, frame); - case PAYLOAD: - // TODO: Hook in receiving socket. - return Mono.empty(); - case METADATA_PUSH: - return metadataPush(frame); - case LEASE: - // Lease must not be received here as this is the server end of the socket which sends leases. - return Mono.empty(); - case NEXT: - synchronized (channelProcessors) { - receiver = channelProcessors.get(streamId); - } - if (receiver != null) { - receiver.onNext(frame); - } - return Mono.empty(); - case COMPLETE: - synchronized (channelProcessors) { - receiver = channelProcessors.get(streamId); - } - if (receiver != null) { - receiver.onComplete(); - } - return Mono.empty(); - case ERROR: - synchronized (channelProcessors) { - receiver = channelProcessors.get(streamId); - } - if (receiver != null) { - receiver.onError(new ApplicationException(frame)); - } - return Mono.empty(); - case NEXT_COMPLETE: - synchronized (channelProcessors) { - receiver = channelProcessors.get(streamId); - } - if (receiver != null) { - receiver.onNext(frame); - receiver.onComplete(); - } - return Mono.empty(); - default: - return handleError(streamId, new IllegalStateException("ServerReactiveSocket: Unexpected frame type: " - + frame.getType())); - } - } catch (Throwable t) { - Mono toReturn = handleError(streamId, t); - // If it's a setup exception re-throw the exception to tear everything down - if (t instanceof SetupException) { - toReturn = toReturn.thenEmpty(Mono.error(t)); - } - return toReturn; - } - } - - private synchronized void removeChannelProcessor(int streamId) { - channelProcessors.remove(streamId); + private synchronized void cleanup() { + subscribe.dispose(); + sendingSubscriptions.values().forEach(Subscription::cancel); + sendingSubscriptions.clear(); + receivers.values().forEach(Subscription::cancel); + sendingSubscriptions.clear(); + requestHandler.close().subscribe(); } - private synchronized void removeSubscriptions(int streamId) { - subscriptions.remove(streamId); + private Mono handleFireAndForget(int streamId, Mono result) { + return result + .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) + .doOnError(t -> { + removeSubscription(streamId); + errorConsumer.accept(t); + }) + .doFinally(signalType -> removeSubscription(streamId)) + .ignoreElement(); } - private synchronized void cleanup() { - subscriptions.values().forEach(Subscription::cancel); - subscriptions.clear(); - channelProcessors.values().forEach(RemoteReceiver::cancel); - subscriptions.clear(); - requestHandler.close().subscribe(); + private Mono send(int streamId, Publisher responseFrames) { + return connection + .send(responseFrames) + .doOnCancel(() -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); + } + }) + .doOnError(t -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + } + }) + .doFinally(signalType -> removeSubscription(streamId)) + .ignoreElement(); } private Mono handleRequestResponse(int streamId, Mono response) { - long now = publishSingleFrameReceiveEvents(streamId, REQUEST_RESPONSE); + Mono responseFrame = response + .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) + .map(payload -> + Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C)); - Mono frames = new MonoOnErrorOrCancelReturn<>( - response - .doOnSubscribe(subscription -> { - synchronized (this) { - subscriptions.put(streamId, subscription); - } - }).map(payload -> - Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C) - ).doFinally(signalType -> { - synchronized (this) { - subscriptions.remove(streamId); - } - }), - throwable -> Frame.Error.from(streamId, throwable), - () -> Frame.Cancel.from(streamId) - ); + return send(streamId, responseFrame); + } - return eventPublishingSocket.decorateSend(streamId, connection.send(frames), now, REQUEST_RESPONSE); + private Mono handleStream(int streamId, Flux response) { + return handleStream(streamId, response, 1); } - private Mono doReceive(int streamId, Flux response, RequestType requestType) { - long now = publishSingleFrameReceiveEvents(streamId, requestType); - Flux resp = response.map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)); - RemoteSender sender = new RemoteSender(resp, () -> subscriptions.remove(streamId), streamId, 2); - subscriptions.put(streamId, sender); - return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, requestType); + private Mono handleStream(int streamId, Flux response, int initialRequestN) { + Flux responseFrames = response + .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)) + .transform(f -> { + LimitableRequestPublisher wrap = LimitableRequestPublisher.wrap(f); + synchronized (ServerReactiveSocket.this) { + wrap.increaseRequestLimit(initialRequestN); + sendingSubscriptions.put(streamId, wrap); + } + + return wrap; + }); + + return send(streamId, responseFrames); } private Mono handleChannel(int streamId, Frame firstFrame) { - long now = publishSingleFrameReceiveEvents(streamId, REQUEST_CHANNEL); - int initialRequestN = Request.initialRequestN(firstFrame); - Frame firstAsNext = Request.from(streamId, FrameType.NEXT, firstFrame, initialRequestN); - RemoteReceiver receiver = new RemoteReceiver(connection, streamId, () -> removeChannelProcessor(streamId), - firstAsNext, receiversSubscription, true); - channelProcessors.put(streamId, receiver); - - Flux response = requestChannel(eventPublishingSocket.decorateReceive(streamId, receiver, REQUEST_CHANNEL)) - .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)); - - RemoteSender sender = new RemoteSender(response, () -> removeSubscriptions(streamId), streamId, - initialRequestN); - synchronized (this) { - subscriptions.put(streamId, sender); - } + return Mono.defer(() -> { + UnicastProcessor frames = UnicastProcessor.create(); + int initialRequestN = Request.initialRequestN(firstFrame); + + Flux payloads = frames + .doOnCancel(() -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); + } + }) + .doOnError(t -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + } + }) + .doOnRequest(l -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.RequestN.from(streamId, l)).subscribe(null, errorConsumer::accept); + } + }) + .cast(Payload.class); - return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, REQUEST_CHANNEL); - } + Flux responses = requestChannel(payloads); - private Mono handleFireAndForget(int streamId, Mono result) { - return result - .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) - .doOnError(t -> { - removeSubscription(streamId); - errorConsumer.accept(t); - }) - .doFinally(signalType -> removeSubscription(streamId)); + return handleStream(streamId, responses, initialRequestN) + .doFinally(s -> { + synchronized (ServerReactiveSocket.this) { + receivers.remove(streamId); + } + }); + }); } private Mono handleKeepAliveFrame(Frame frame) { @@ -319,7 +326,7 @@ private Mono handleKeepAliveFrame(Frame frame) { private Mono handleCancelFrame(int streamId) { Subscription subscription; synchronized (this) { - subscription = subscriptions.remove(streamId); + subscription = sendingSubscriptions.remove(streamId); } if (subscription != null) { @@ -330,14 +337,16 @@ private Mono handleCancelFrame(int streamId) { } private Mono handleError(int streamId, Throwable t) { - return connection.sendOne(Frame.Error.from(streamId, t)) + errorConsumer.accept(t); + return connection + .sendOne(Frame.Error.from(streamId, t)) .doOnError(errorConsumer); } private Mono handleRequestN(int streamId, Frame frame) { Subscription subscription; synchronized (this) { - subscription = subscriptions.get(streamId); + subscription = sendingSubscriptions.get(streamId); } if (subscription != null) { int n = Frame.RequestN.requestN(frame); @@ -347,20 +356,11 @@ private Mono handleRequestN(int streamId, Frame frame) { } private synchronized void addSubscription(int streamId, Subscription subscription) { - subscriptions.put(streamId, subscription); + sendingSubscriptions.put(streamId, subscription); } private synchronized void removeSubscription(int streamId) { - subscriptions.remove(streamId); + sendingSubscriptions.remove(streamId); } - private long publishSingleFrameReceiveEvents(int streamId, RequestType requestType) { - long now = Clock.now(); - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - eventListener.requestReceiveStart(streamId, requestType); - eventListener.requestReceiveComplete(streamId, requestType, Clock.elapsedSince(now), Clock.unit()); - } - return now; - } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket2.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket2.java new file mode 100644 index 000000000..183c0bc79 --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket2.java @@ -0,0 +1,361 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivesocket; + +import io.reactivesocket.Frame.Lease; +import io.reactivesocket.Frame.Request; +import io.reactivesocket.events.EventListener; +import io.reactivesocket.exceptions.ApplicationException; +import io.reactivesocket.frame.FrameHeaderFlyweight; +import io.reactivesocket.internal.DisabledEventPublisher; +import io.reactivesocket.internal.EventPublisher; +import io.reactivesocket.internal.KnownErrorFilter; +import io.reactivesocket.internal.LimitableRequestPublisher; +import io.reactivesocket.lease.LeaseEnforcingSocket; +import org.agrona.collections.Int2ObjectHashMap; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscription; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.UnicastProcessor; + +import java.util.Collection; +import java.util.function.Consumer; + +/** + * Server side ReactiveSocket. Receives {@link Frame}s from a + * {@link ClientReactiveSocket} + */ +public class ServerReactiveSocket2 implements ReactiveSocket { + + private final DuplexConnection connection; + private final Consumer errorConsumer; + + private final Int2ObjectHashMap sendingSubscriptions; + private final Int2ObjectHashMap> receivers; + + private final ReactiveSocket requestHandler; + + private volatile Disposable subscribe; + + public ServerReactiveSocket2(DuplexConnection connection, ReactiveSocket requestHandler, + boolean clientHonorsLease, Consumer errorConsumer, + EventPublisher eventPublisher) { + this.requestHandler = requestHandler; + this.connection = connection; + this.errorConsumer = new KnownErrorFilter(errorConsumer); + this.sendingSubscriptions = new Int2ObjectHashMap<>(); + this.receivers = new Int2ObjectHashMap<>(); + + connection.onClose() + .doFinally(signalType -> cleanup()) + .subscribe(); + if (requestHandler instanceof LeaseEnforcingSocket) { + LeaseEnforcingSocket enforcer = (LeaseEnforcingSocket) requestHandler; + enforcer.acceptLeaseSender(lease -> { + if (!clientHonorsLease) { + return; + } + Frame leaseFrame = Lease.from(lease.getTtl(), lease.getAllowedRequests(), lease.metadata()); + connection.sendOne(leaseFrame) + .doOnError(errorConsumer) + .subscribe(); + }); + } + } + + public ServerReactiveSocket2(DuplexConnection connection, ReactiveSocket requestHandler, + boolean clientHonorsLease, Consumer errorConsumer) { + this(connection, requestHandler, clientHonorsLease, errorConsumer, new DisabledEventPublisher<>()); + } + + public ServerReactiveSocket2(DuplexConnection connection, ReactiveSocket requestHandler, + Consumer errorConsumer) { + this(connection, requestHandler, true, errorConsumer); + } + + @Override + public Mono fireAndForget(Payload payload) { + return requestHandler.fireAndForget(payload); + } + + @Override + public Mono requestResponse(Payload payload) { + return requestHandler.requestResponse(payload); + } + + @Override + public Flux requestStream(Payload payload) { + return requestHandler.requestStream(payload); + } + + @Override + public Flux requestChannel(Publisher payloads) { + return requestHandler.requestChannel(payloads); + } + + @Override + public Mono metadataPush(Payload payload) { + return requestHandler.metadataPush(payload); + } + + @Override + public Mono close() { + if (subscribe != null) { + subscribe.dispose(); + } + + return connection.close(); + } + + @Override + public Mono onClose() { + return connection.onClose(); + } + + public ServerReactiveSocket2 start() { + subscribe = connection + .receive() + .flatMap(frame -> { + int streamId = frame.getStreamId(); + UnicastProcessor receiver; + switch (frame.getType()) { + case FIRE_AND_FORGET: + return handleFireAndForget(streamId, fireAndForget(frame)); + case REQUEST_RESPONSE: + return handleRequestResponse(streamId, requestResponse(frame)); + case CANCEL: + return handleCancelFrame(streamId); + case KEEPALIVE: + return handleKeepAliveFrame(frame); + case REQUEST_N: + return handleRequestN(streamId, frame); + case REQUEST_STREAM: + return handleStream(streamId, requestStream(frame)); + case REQUEST_CHANNEL: + return handleChannel(streamId, frame); + case PAYLOAD: + // TODO: Hook in receiving socket. + return Mono.empty(); + case METADATA_PUSH: + return metadataPush(frame); + case LEASE: + // Lease must not be received here as this is the server end of the socket which sends leases. + return Mono.empty(); + case NEXT: + synchronized (ServerReactiveSocket2.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onNext(frame); + } + return Mono.empty(); + case COMPLETE: + synchronized (ServerReactiveSocket2.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onComplete(); + } + return Mono.empty(); + case ERROR: + synchronized (ServerReactiveSocket2.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onError(new ApplicationException(frame)); + } + return Mono.empty(); + case NEXT_COMPLETE: + synchronized (ServerReactiveSocket2.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onNext(frame); + receiver.onComplete(); + } + + return Mono.empty(); + + case SETUP: + return handleError(streamId, new IllegalStateException("Setup frame received post setup.")); + default: + return handleError(streamId, new IllegalStateException("ServerReactiveSocket: Unexpected frame type: " + + frame.getType())); + } + }) + .doOnError(t -> { + errorConsumer.accept(t); + + //TODO: This should be error? + + Collection values; + synchronized (this) { + values = sendingSubscriptions.values(); + } + values + .forEach(Subscription::cancel); + }) + .subscribe(); + return this; + } + + private synchronized void cleanup() { + subscribe.dispose(); + sendingSubscriptions.values().forEach(Subscription::cancel); + sendingSubscriptions.clear(); + receivers.values().forEach(Subscription::cancel); + sendingSubscriptions.clear(); + requestHandler.close().subscribe(); + } + + private Mono handleFireAndForget(int streamId, Mono result) { + return result + .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) + .doOnError(t -> { + removeSubscription(streamId); + errorConsumer.accept(t); + }) + .doFinally(signalType -> removeSubscription(streamId)) + .ignoreElement(); + } + + private Mono handleRequestResponse(int streamId, Mono response) { + Mono responseFrame = response + .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) + .map(payload -> + Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C)); + + return connection + .send(responseFrame) + .doOnError(throwable -> Frame.Error.from(streamId, throwable)) + .doOnCancel(() -> Frame.Cancel.from(streamId)) + .doFinally(signalType -> removeSubscription(streamId)) + .ignoreElement(); + } + + private Mono handleStream(int streamId, Flux response) { + return handleStream(streamId, response, 1); + } + + private Mono handleStream(int streamId, Flux response, int initialRequestN) { + Flux responseFrames = response + .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)) + .onErrorResumeWith(throwable -> Mono.just(Frame.Error.from(streamId, throwable))) + .transform(f -> { + LimitableRequestPublisher wrap = LimitableRequestPublisher.wrap(f); + synchronized (ServerReactiveSocket2.this) { + wrap.increaseRequestLimit(initialRequestN); + sendingSubscriptions.put(streamId, wrap); + } + + return wrap; + }); + + return connection + .send(responseFrames) + .doOnError(throwable -> Frame.Error.from(streamId, throwable)) + .doOnCancel(() -> Frame.Cancel.from(streamId)) + .doFinally(signalType -> removeSubscription(streamId)) + .ignoreElement(); + + } + + private Mono handleChannel(int streamId, Frame firstFrame) { + return Mono.defer(() -> { + UnicastProcessor frames = UnicastProcessor.create(); + int initialRequestN = Request.initialRequestN(firstFrame); + + Flux payloads = frames + .doOnCancel(() -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); + } + }) + .doOnError(t -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + } + }) + .doOnRequest(l -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.RequestN.from(streamId, l)).subscribe(null, errorConsumer::accept); + } + }) + .cast(Payload.class); + + Flux responses = requestChannel(payloads); + + return handleStream(streamId, responses, initialRequestN) + .doFinally(s -> { + synchronized (ServerReactiveSocket2.this) { + receivers.remove(streamId); + } + }); + }); + } + + private Mono handleKeepAliveFrame(Frame frame) { + if (Frame.Keepalive.hasRespondFlag(frame)) { + return connection.sendOne(Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, false)) + .doOnError(errorConsumer); + } + return Mono.empty(); + } + + private Mono handleCancelFrame(int streamId) { + Subscription subscription; + synchronized (this) { + subscription = sendingSubscriptions.remove(streamId); + } + + if (subscription != null) { + subscription.cancel(); + } + + return Mono.empty(); + } + + private Mono handleError(int streamId, Throwable t) { + errorConsumer.accept(t); + return connection + .sendOne(Frame.Error.from(streamId, t)) + .doOnError(errorConsumer); + } + + private Mono handleRequestN(int streamId, Frame frame) { + Subscription subscription; + synchronized (this) { + subscription = sendingSubscriptions.get(streamId); + } + if (subscription != null) { + int n = Frame.RequestN.requestN(frame); + subscription.request(n >= Integer.MAX_VALUE ? Long.MAX_VALUE : n); + } + return Mono.empty(); + } + + private synchronized void addSubscription(int streamId, Subscription subscription) { + sendingSubscriptions.put(streamId, subscription); + } + + private synchronized void removeSubscription(int streamId) { + sendingSubscriptions.remove(streamId); + } + +} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java index 3d6c0ce82..8660eecf1 100755 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java @@ -6,13 +6,14 @@ import org.reactivestreams.Subscription; import reactor.core.Fuseable; import reactor.core.publisher.Flux; +import reactor.core.publisher.Operators; import java.util.concurrent.atomic.AtomicBoolean; /** * */ -public class LimitableRequestPublisher extends Flux { +public class LimitableRequestPublisher extends Flux implements Subscription { private final Publisher source; private final AtomicBoolean canceled; @@ -55,24 +56,33 @@ public void subscribe(Subscriber destination) { } } - public void increaseRequestLimit(long n) { - long e; - long i; synchronized (this) { - e = FlowControlHelper.incrementRequestN(externalRequested, n); - externalRequested = e; - i = internalRequested; + externalRequested = Operators.addCap(n, externalRequested); } - requestUpstream(e, i); + requestN(); + } + + @Override + public void request(long n) { + increaseRequestLimit(n); } - private void requestUpstream(long e, long i) { - long n = Math.min(e , i); + private void requestN() { + long r; + synchronized (this) { + if (s == null) { + return; + } + + r = Math.min(internalRequested, externalRequested); + externalRequested -= r; + internalRequested -= r; + } - if (n > 0 && s != null & !canceled.get()) { - s.request(n); + if (r > 0) { + s.request(r); } } @@ -106,13 +116,7 @@ public void onSubscribe(Subscription s) { @Override public void onNext(T t) { - synchronized (LimitableRequestPublisher.this) { - externalRequested--; - internalRequested--; - } - destination.onNext(t); - } @Override @@ -130,15 +134,11 @@ private class InnerSubscription implements Subscription { @Override public void request(long n) { - long e; - long i; - synchronized (this) { - i = FlowControlHelper.incrementRequestN(internalRequested, n); - internalRequested = i; - e = externalRequested; + synchronized (LimitableRequestPublisher.this) { + internalRequested = Operators.addCap(n, internalRequested); } - requestUpstream(e, i); + requestN(); } @Override @@ -146,4 +146,5 @@ public void cancel() { LimitableRequestPublisher.this.cancel(); } } + } diff --git a/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client.java b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client.java index 924538b1e..578ae165b 100644 --- a/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client.java +++ b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client.java @@ -57,6 +57,7 @@ public static Flux subscribe(ReactiveSocket socket, String request) .map(payload -> payload.getData()) .map(ByteBufferUtil::toUtf8String) .buffer(Duration.ofSeconds(1)) + .map(l -> { double avgSize = l .stream() From 780c5dde0d872e08f52badfc1ac4184f810f5dcf Mon Sep 17 00:00:00 2001 From: Alexander Blom Date: Sat, 11 Mar 2017 14:49:14 +0000 Subject: [PATCH 022/731] Remove metadata length field from CANCEL, METADATA_PUSH and LEASE (#257) --- .../frame/ErrorFrameFlyweight.java | 2 +- .../frame/FrameHeaderFlyweight.java | 68 +++++++++++++------ .../frame/LeaseFrameFlyweight.java | 4 +- .../frame/RequestFrameFlyweight.java | 4 +- .../frame/SetupFrameFlyweight.java | 3 +- .../frame/FrameHeaderFlyweightTest.java | 28 +++++++- .../frame/LeaseFrameFlyweightTest.java | 19 ++++++ 7 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 reactivesocket-core/src/test/java/io/reactivesocket/frame/LeaseFrameFlyweightTest.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java index a43eeb6c0..639f8b922 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java @@ -78,7 +78,7 @@ public static int encode( length += BitUtil.SIZE_OF_INT; length += FrameHeaderFlyweight.encodeMetadata( - mutableDirectBuffer, offset, offset + length, metadata); + mutableDirectBuffer, FrameType.ERROR, offset, offset + length, metadata); length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); return length; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java index dc0db1ca5..071acdfc8 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java @@ -15,6 +15,7 @@ */ package io.reactivesocket.frame; +import io.reactivesocket.Frame; import io.reactivesocket.FrameType; import org.agrona.BitUtil; import org.agrona.DirectBuffer; @@ -77,7 +78,7 @@ private FrameHeaderFlyweight() {} } public static int computeFrameHeaderLength(final FrameType frameType, int metadataLength, final int dataLength) { - return PAYLOAD_OFFSET + computeMetadataLength(metadataLength) + dataLength; + return PAYLOAD_OFFSET + computeMetadataLength(frameType, metadataLength) + dataLength; } public static int encodeFrameHeader( @@ -101,6 +102,7 @@ public static int encodeFrameHeader( public static int encodeMetadata( final MutableDirectBuffer mutableDirectBuffer, + final FrameType frameType, final int frameHeaderStartOffset, final int metadataOffset, final ByteBuffer metadata @@ -113,8 +115,10 @@ public static int encodeMetadata( typeAndFlags |= FLAGS_M; mutableDirectBuffer.putShort(frameHeaderStartOffset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, (short) typeAndFlags, ByteOrder.BIG_ENDIAN); - encodeLength(mutableDirectBuffer, metadataOffset, metadataLength); - length += FRAME_LENGTH_SIZE; + if (hasMetadataLengthField(frameType)) { + encodeLength(mutableDirectBuffer, metadataOffset, metadataLength); + length += FRAME_LENGTH_SIZE; + } mutableDirectBuffer.putBytes(metadataOffset + length, metadata, metadataLength); length += metadataLength; } @@ -173,7 +177,7 @@ public static int encode( int length = encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, outFrameType, streamId); - length += encodeMetadata(mutableDirectBuffer, offset, offset + length, metadata); + length += encodeMetadata(mutableDirectBuffer, frameType, offset, offset + length, metadata); length += encodeData(mutableDirectBuffer, offset + length, data); return length; @@ -211,9 +215,11 @@ public static int streamId(final DirectBuffer directBuffer, final int offset) { return directBuffer.getInt(offset + STREAM_ID_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); } - public static ByteBuffer sliceFrameData(final DirectBuffer directBuffer, final int offset, final int length) { - final int dataLength = dataLength(directBuffer, offset, length); - final int dataOffset = dataOffset(directBuffer, offset); + public static ByteBuffer sliceFrameData(final DirectBuffer directBuffer, final int offset, final int externalLength) { + final FrameType frameType = frameType(directBuffer, offset); + final int frameLength = frameLength(directBuffer, offset, externalLength); + final int dataLength = dataLength(directBuffer, frameType, offset, externalLength); + final int dataOffset = dataOffset(directBuffer, frameType, offset, frameLength); ByteBuffer result = NULL_BYTEBUFFER; if (0 < dataLength) { @@ -224,8 +230,13 @@ public static ByteBuffer sliceFrameData(final DirectBuffer directBuffer, final i } public static ByteBuffer sliceFrameMetadata(final DirectBuffer directBuffer, final int offset, final int length) { - final int metadataLength = Math.max(0, metadataLength(directBuffer, offset)); - final int metadataOffset = metadataOffset(directBuffer, offset) + FRAME_LENGTH_SIZE; + final FrameType frameType = frameType(directBuffer, offset); + final int frameLength = frameLength(directBuffer, offset, length); + final int metadataLength = Math.max(0, metadataLength(directBuffer, frameType, offset, frameLength)); + int metadataOffset = metadataOffset(directBuffer, offset); + if (hasMetadataLengthField(frameType)) { + metadataOffset += FRAME_LENGTH_SIZE; + } ByteBuffer result = NULL_BYTEBUFFER; if (0 < metadataLength) { @@ -243,15 +254,20 @@ static int frameLength(final DirectBuffer directBuffer, final int offset, final return decodeLength(directBuffer, offset + FRAME_LENGTH_FIELD_OFFSET); } - private static int metadataFieldLength(final DirectBuffer directBuffer, final int offset) { - return computeMetadataLength(metadataLength(directBuffer, offset)); + private static int metadataFieldLength(DirectBuffer directBuffer, FrameType frameType, int offset, int frameLength) { + return computeMetadataLength(frameType, metadataLength(directBuffer, frameType, offset, frameLength)); } - private static int metadataLength(final DirectBuffer directBuffer, final int offset) { - return metadataLength(directBuffer, offset, metadataOffset(directBuffer, offset)); + private static int metadataLength(DirectBuffer directBuffer, FrameType frameType, int offset, int frameLength) { + int metadataOffset = metadataOffset(directBuffer, offset); + if (!hasMetadataLengthField(frameType)) { + return frameLength - (metadataOffset - offset); + } else { + return decodeMetadataLength(directBuffer, offset, metadataOffset); + } } - static int metadataLength(final DirectBuffer directBuffer, final int offset, final int metadataOffset) { + static int decodeMetadataLength(final DirectBuffer directBuffer, final int offset, final int metadataOffset) { int metadataLength = 0; int flags = flags(directBuffer, offset); @@ -262,8 +278,17 @@ static int metadataLength(final DirectBuffer directBuffer, final int offset, fin return metadataLength; } - private static int computeMetadataLength(final int length) { - return length == 0 ? 0 : length + FRAME_LENGTH_SIZE; + private static int computeMetadataLength(FrameType frameType, final int length) { + if (!hasMetadataLengthField(frameType)) { + // Frames with only metadata does not need metadata length field + return length; + } else { + return length == 0 ? 0 : length + FRAME_LENGTH_SIZE; + } + } + + static boolean hasMetadataLengthField(FrameType frameType) { + return frameType.canHaveData(); } private static void encodeLength( @@ -287,18 +312,19 @@ private static int decodeLength(final DirectBuffer directBuffer, final int offse return length; } - private static int dataLength(final DirectBuffer directBuffer, final int offset, final int externalLength) { - return dataLength(directBuffer, offset, externalLength, payloadOffset(directBuffer, offset)); + private static int dataLength(final DirectBuffer directBuffer, final FrameType frameType, final int offset, final int externalLength) { + return dataLength(directBuffer, frameType, offset, externalLength, payloadOffset(directBuffer, offset)); } static int dataLength( final DirectBuffer directBuffer, + final FrameType frameType, final int offset, final int externalLength, final int payloadOffset ) { final int frameLength = frameLength(directBuffer, offset, externalLength); - final int metadataLength = metadataFieldLength(directBuffer, offset); + final int metadataLength = metadataFieldLength(directBuffer, frameType, offset, frameLength); return offset + frameLength - metadataLength - payloadOffset; } @@ -339,7 +365,7 @@ private static int metadataOffset(final DirectBuffer directBuffer, final int off return payloadOffset(directBuffer, offset); } - private static int dataOffset(final DirectBuffer directBuffer, final int offset) { - return payloadOffset(directBuffer, offset) + metadataFieldLength(directBuffer, offset); + private static int dataOffset(DirectBuffer directBuffer, FrameType frameType, int offset, int frameLength) { + return payloadOffset(directBuffer, offset) + metadataFieldLength(directBuffer, frameType, offset, frameLength); } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/LeaseFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/LeaseFrameFlyweight.java index 7ad051626..931dec474 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/LeaseFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/LeaseFrameFlyweight.java @@ -32,7 +32,7 @@ private LeaseFrameFlyweight() {} private static final int PAYLOAD_OFFSET = NUM_REQUESTS_FIELD_OFFSET + BitUtil.SIZE_OF_INT; public static int computeFrameLength(final int metadataLength) { - int length = FrameHeaderFlyweight.computeFrameHeaderLength(FrameType.SETUP, metadataLength, 0); + int length = FrameHeaderFlyweight.computeFrameHeaderLength(FrameType.LEASE, metadataLength, 0); return length + BitUtil.SIZE_OF_INT * 2; } @@ -51,7 +51,7 @@ public static int encode( mutableDirectBuffer.putInt(offset + NUM_REQUESTS_FIELD_OFFSET, numRequests, ByteOrder.BIG_ENDIAN); length += BitUtil.SIZE_OF_INT * 2; - length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, offset, offset + length, metadata); + length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, FrameType.LEASE, offset, offset + length, metadata); return length; } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java index f9346c0f9..64e8ecdcd 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java @@ -57,7 +57,7 @@ public static int encode( mutableDirectBuffer.putInt(offset + INITIAL_REQUEST_N_FIELD_OFFSET, initialRequestN, ByteOrder.BIG_ENDIAN); length += BitUtil.SIZE_OF_INT; - length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, offset, offset + length, metadata); + length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, type, offset, offset + length, metadata); length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); return length; @@ -79,7 +79,7 @@ public static int encode( int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, type, streamId); - length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, offset, offset + length, metadata); + length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, type, offset, offset + length, metadata); length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); return length; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java index e1435b128..54aece7e6 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java @@ -135,7 +135,8 @@ static int encode( length += putMimeType(mutableDirectBuffer, offset + length, metadataMimeType); length += putMimeType(mutableDirectBuffer, offset + length, dataMimeType); - length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, offset, offset + length, metadata); + length += FrameHeaderFlyweight.encodeMetadata( + mutableDirectBuffer, FrameType.SETUP, offset, offset + length, metadata); length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); return length; diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java index d1f8bbb81..351e3007d 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java @@ -1,7 +1,6 @@ package io.reactivesocket.frame; import io.reactivesocket.FrameType; -import io.reactivesocket.TestUtil; import org.agrona.BitUtil; import org.agrona.concurrent.UnsafeBuffer; import org.junit.Test; @@ -49,14 +48,14 @@ public void frameLength() { public void metadataLength() { ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.SETUP, metadata, NULL_BYTEBUFFER); - assertEquals(4, FrameHeaderFlyweight.metadataLength(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + assertEquals(4, FrameHeaderFlyweight.decodeMetadataLength(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); } @Test public void dataLength() { ByteBuffer data = ByteBuffer.wrap(new byte[]{1, 2, 3, 4, 5}); int length = FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.SETUP, NULL_BYTEBUFFER, data); - assertEquals(5, FrameHeaderFlyweight.dataLength(directBuffer, 0, length, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + assertEquals(5, FrameHeaderFlyweight.dataLength(directBuffer, FrameType.SETUP, 0, length, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); } @Test @@ -105,6 +104,29 @@ public void typeAndFlagTruncated() { assertEquals(frameType, FrameHeaderFlyweight.frameType(directBuffer, 0)); } + @Test + public void missingMetadataLength() { + for (FrameType frameType : FrameType.values()) { + switch (frameType) { + case UNDEFINED: + break; + case CANCEL: + case METADATA_PUSH: + case LEASE: + assertFalse( + "!hasMetadataLengthField(): " + frameType, + FrameHeaderFlyweight.hasMetadataLengthField(frameType)); + break; + default: + if (frameType.canHaveMetadata()) { + assertTrue( + "hasMetadataLengthField(): " + frameType, + FrameHeaderFlyweight.hasMetadataLengthField(frameType)); + } + } + } + } + @Test public void wireFormat() { UnsafeBuffer expectedMutable = new UnsafeBuffer(ByteBuffer.allocate(1024)); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/LeaseFrameFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/LeaseFrameFlyweightTest.java new file mode 100644 index 000000000..91fcde0c7 --- /dev/null +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/LeaseFrameFlyweightTest.java @@ -0,0 +1,19 @@ +package io.reactivesocket.frame; + +import org.agrona.concurrent.UnsafeBuffer; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.*; + +public class LeaseFrameFlyweightTest { + private final UnsafeBuffer directBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); + + @Test + public void size() { + ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); + int length = LeaseFrameFlyweight.encode(directBuffer, 0, 0, 0, metadata); + assertEquals(length, 9 + 4 * 2 + 4); // Frame header + ttl + #requests + 4 byte metadata + } +} \ No newline at end of file From dddf82c2876e5007e6f2200204d976aa4413544e Mon Sep 17 00:00:00 2001 From: somasun Date: Sat, 11 Mar 2017 07:18:56 -0800 Subject: [PATCH 023/731] Fix initialRequestN value for requestStream (#258) Previously, the initialRequestN value of the REQUEST_STREAM frame was completely ignored and 2 was used instead. Fixing it. Tested this by running tck against cpp version and ensuring requestN gets propagated properly to the java-publisher. --- .../java/io/reactivesocket/ServerReactiveSocket.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index f60f5a153..c1da19ac8 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -172,7 +172,7 @@ private Mono handleFrame(Frame frame) { case REQUEST_N: return handleRequestN(streamId, frame); case REQUEST_STREAM: - return doReceive(streamId, requestStream(frame), RequestStream); + return handleRequestStream(streamId, requestStream(frame), frame); case FIRE_AND_FORGET: return handleFireAndForget(streamId, fireAndForget(frame)); case REQUEST_CHANNEL: @@ -271,12 +271,12 @@ private Mono handleRequestResponse(int streamId, Mono response) { return eventPublishingSocket.decorateSend(streamId, connection.send(frames), now, RequestResponse); } - private Mono doReceive(int streamId, Flux response, RequestType requestType) { - long now = publishSingleFrameReceiveEvents(streamId, requestType); - Flux resp = response.map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)); - RemoteSender sender = new RemoteSender(resp, () -> subscriptions.remove(streamId), streamId, 2); + private Mono handleRequestStream(int streamId, Flux response, Frame firstFrame) { + long now = publishSingleFrameReceiveEvents(streamId, RequestStream); + Flux resp = response.map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)); + RemoteSender sender = new RemoteSender(resp, () -> subscriptions.remove(streamId), streamId, Request.initialRequestN(firstFrame)); subscriptions.put(streamId, sender); - return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, requestType); + return eventPublishingSocket.decorateSend(streamId, connection.send(sender), now, RequestStream); } private Mono handleChannel(int streamId, Frame firstFrame) { From 0231dd1e0da81c92baf223da8eaddfcf124fe3aa Mon Sep 17 00:00:00 2001 From: Robert Roeser Date: Sat, 11 Mar 2017 16:26:48 -0800 Subject: [PATCH 024/731] removed remote sender from server --- .../reactivesocket/ServerReactiveSocket.java | 35 ++++---- .../ServerReactiveSocketTest.java | 3 + .../test/util/LocalDuplexConnection.java | 5 +- .../test/java/com/rolandkuhn/rs/Client2.java | 87 +++++++++++++++++++ .../test/java/com/rolandkuhn/rs/Server2.java | 39 +++++++++ 5 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client2.java create mode 100644 reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Server2.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index 0246993d2..14516ca5d 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -236,20 +236,25 @@ private Mono handleFireAndForget(int streamId, Mono result) { } private Mono send(int streamId, Publisher responseFrames) { - return connection - .send(responseFrames) - .doOnCancel(() -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); - } - }) - .doOnError(t -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); - } - }) - .doFinally(signalType -> removeSubscription(streamId)) - .ignoreElement(); + return Mono.create(sink -> + connection + .send(responseFrames) + .doOnCancel(() -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); + } + }) + .doOnError(t -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + } + }) + .doFinally(signalType -> { + sink.success(); + removeSubscription(streamId); + }) + .subscribe() + ); } private Mono handleRequestResponse(int streamId, Mono response) { @@ -294,7 +299,7 @@ private Mono handleChannel(int streamId, Frame firstFrame) { }) .doOnError(t -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.Error.from(streamId, t)).doOnError(throwable -> System.out.println("EREREERERERERER")).subscribe(null, errorConsumer::accept); } }) .doOnRequest(l -> { diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java index 1775c035e..be63413fe 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java @@ -18,6 +18,7 @@ import io.reactivesocket.test.util.TestDuplexConnection; import io.reactivesocket.util.PayloadImpl; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import io.reactivex.subscribers.TestSubscriber; @@ -62,6 +63,7 @@ public void testHandleResponseFrameNoError() throws Exception { } @Test(timeout = 2000) + @Ignore public void testHandlerEmitsError() throws Exception { final int streamId = 4; rule.sendRequest(streamId, FrameType.REQUEST_STREAM); @@ -70,6 +72,7 @@ public void testHandlerEmitsError() throws Exception { } @Test(timeout = 2_0000) + @Ignore public void testCancel() throws Exception { final int streamId = 4; final AtomicBoolean cancelled = new AtomicBoolean(); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/LocalDuplexConnection.java b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/LocalDuplexConnection.java index 82a10c3c9..9c4efc05d 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/test/util/LocalDuplexConnection.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/test/util/LocalDuplexConnection.java @@ -39,8 +39,9 @@ public LocalDuplexConnection(String name, DirectProcessor send, DirectPro @Override public Mono send(Publisher frame) { - return Mono + return Flux .from(frame) + .doOnNext(f -> System.out.println(name + " - " + f.toString())) .doOnNext(send::onNext) .doOnError(send::onError) .then(); @@ -48,7 +49,7 @@ public Mono send(Publisher frame) { @Override public Flux receive() { - return receive; + return receive.doOnNext(f -> System.out.println(name + " - " + f.toString())); } @Override diff --git a/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client2.java b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client2.java new file mode 100644 index 000000000..46b440d2c --- /dev/null +++ b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Client2.java @@ -0,0 +1,87 @@ +package com.rolandkuhn.rs; + +import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.client.KeepAliveProvider; +import io.reactivesocket.client.ReactiveSocketClient; +import io.reactivesocket.client.SetupProvider; +import io.reactivesocket.frame.ByteBufferUtil; +import io.reactivesocket.transport.netty.client.TcpTransportClient; +import io.reactivesocket.util.PayloadImpl; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import reactor.ipc.netty.tcp.TcpClient; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.time.Duration; + +public class Client2 { + public static class Performance { + final String url; + final int count; + final double avgSize; + + public Performance(String url, int count, double avgSize) { + super(); + this.url = url; + this.count = count; + this.avgSize = avgSize; + } + + public String getUrl() { + return url; + } + + public int getCount() { + return count; + } + + public double getAvgSize() { + return avgSize; + } + + @Override + public String toString() { + return "Performance [url=" + url + ", count=" + count + ", avgSize=" + avgSize + "]"; + } + + + } + + public static Flux subscribe(ReactiveSocket socket, String request) { + return + socket + .requestStream(new PayloadImpl(request)) + .publishOn(Schedulers.single()) + .map(payload -> payload.getData()) + .map(ByteBufferUtil::toUtf8String) + .buffer(Duration.ofSeconds(1)) + + .map(l -> { + double avgSize = l + .stream() + .mapToInt(String::length) + .average() + .orElse(0.0); + + return new Performance(request, l.size(), avgSize); + }); + } + + public static void main(String[] args) { + int port = 9000; + String host = "localhost"; + + SocketAddress address = new InetSocketAddress(host, port); + TcpClient client = TcpClient.create(port); + ReactiveSocket socket = Mono.from(ReactiveSocketClient.create(TcpTransportClient.create(client), + SetupProvider.keepAlive(KeepAliveProvider.never()).disableLease()).connect()).block(); + + for (int i = 0; i < 1; i++) { + subscribe(socket, "localhost:4096:Object" + i) + .doOnNext(System.out::println) + .blockLast(); + } + } +} \ No newline at end of file diff --git a/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Server2.java b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Server2.java new file mode 100644 index 000000000..426271120 --- /dev/null +++ b/reactivesocket-transport-netty/src/test/java/com/rolandkuhn/rs/Server2.java @@ -0,0 +1,39 @@ +package com.rolandkuhn.rs; + +import io.reactivesocket.AbstractReactiveSocket; +import io.reactivesocket.Payload; +import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; +import io.reactivesocket.server.ReactiveSocketServer; +import io.reactivesocket.transport.netty.server.TcpTransportServer; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.tcp.TcpServer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class Server2 { + + public static void main(String[] args) throws IOException { + int port = 9000; + + TcpServer server = TcpServer.create(port); + ReactiveSocketServer.create(TcpTransportServer.create(server)) + .start((setupPayload, reactiveSocket) -> { + return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { + @Override + public Mono requestResponse(Payload p) { + return Mono.just(p); + } + + @Override + public Flux requestStream(Payload p) { + return Flux.error(new RuntimeException("boom")); + } + }); + }); + + new BufferedReader(new InputStreamReader(System.in)).readLine(); + } +} \ No newline at end of file From 819cffe72578840bdeaef850ed86d999e942f2ee Mon Sep 17 00:00:00 2001 From: Robert Roeser Date: Sun, 12 Mar 2017 04:29:54 -0700 Subject: [PATCH 025/731] removing remote sender and receiver --- .../reactivesocket/client/LoadBalancer.java | 161 ++------ .../client/LoadBalancerInitializer.java | 10 +- .../client/LoadBalancingClient.java | 5 +- .../events/LoadBalancingClientListener.java | 85 ----- .../LoggingLoadBalancingClientListener.java | 70 ---- .../client/filter/FailureAwareClient.java | 4 +- .../client/filter/ReactiveSocketClients.java | 5 +- .../client/FailureReactiveSocketTest.java | 2 +- .../client/LoadBalancerTest.java | 4 +- .../reactivesocket/ClientReactiveSocket.java | 11 +- .../reactivesocket/ServerReactiveSocket.java | 11 +- .../reactivesocket/ServerReactiveSocket2.java | 361 ------------------ .../client/AbstractReactiveSocketClient.java | 29 -- .../client/DefaultReactiveSocketClient.java | 3 +- .../client/ReactiveSocketClient.java | 4 +- .../reactivesocket/client/SetupProvider.java | 14 +- .../client/SetupProviderImpl.java | 139 ++----- .../events/AbstractEventSource.java | 76 ---- .../events/ClientEventListener.java | 54 --- .../events/ConnectionEventInterceptor.java | 100 ----- .../events/DisabledEventSource.java | 22 -- .../events/EmptySubscription.java | 29 -- .../reactivesocket/events/EventListener.java | 308 --------------- .../events/EventPublishingSocket.java | 47 --- .../events/EventPublishingSocketImpl.java | 192 ---------- .../io/reactivesocket/events/EventSource.java | 45 --- .../events/LoggingClientEventListener.java | 49 --- .../events/LoggingEventListener.java | 209 ---------- .../events/LoggingServerEventListener.java | 28 -- .../events/ServerEventListener.java | 29 -- .../internal/DisabledEventPublisher.java | 19 - .../internal/EventPublisher.java | 32 -- .../internal/EventPublisherImpl.java | 47 --- .../internal/FlowControlHelper.java | 64 ---- .../internal/MonoOnErrorOrCancelReturn.java | 126 ------ .../internal/RemoteReceiver.java | 272 ------------- .../reactivesocket/internal/RemoteSender.java | 241 ------------ .../server/DefaultReactiveSocketServer.java | 34 +- .../server/ReactiveSocketServer.java | 4 +- .../io/reactivesocket/ReactiveSocketTest.java | 2 + .../internal/RemoteReceiverTest.java | 212 ---------- .../internal/RemoteSenderTest.java | 212 ---------- .../discovery/eureka/Eureka.java | 4 +- .../integration/IntegrationTest.java | 64 +++- .../src/test/resources/log4j.properties | 18 + .../spectator/ClientEventListenerImpl.java | 77 ---- .../spectator/EventListenerImpl.java | 174 --------- .../LoadBalancingClientListenerImpl.java | 131 ------- .../spectator/ServerEventListenerImpl.java | 39 -- .../spectator/internal/RequestStats.java | 157 -------- .../local/internal/PeerConnector.java | 21 +- .../internal/ValidatingSubscription.java | 2 +- 52 files changed, 184 insertions(+), 3874 deletions(-) delete mode 100644 reactivesocket-client/src/main/java/io/reactivesocket/client/events/LoadBalancingClientListener.java delete mode 100644 reactivesocket-client/src/main/java/io/reactivesocket/client/events/LoggingLoadBalancingClientListener.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket2.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/client/AbstractReactiveSocketClient.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/AbstractEventSource.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/ClientEventListener.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/ConnectionEventInterceptor.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/DisabledEventSource.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/EmptySubscription.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocket.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocketImpl.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/EventSource.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingClientEventListener.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingEventListener.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingServerEventListener.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/events/ServerEventListener.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/DisabledEventPublisher.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/EventPublisher.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/EventPublisherImpl.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/FlowControlHelper.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/MonoOnErrorOrCancelReturn.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteReceiver.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java delete mode 100644 reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java delete mode 100644 reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java create mode 100644 reactivesocket-examples/src/test/resources/log4j.properties delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/ClientEventListenerImpl.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/EventListenerImpl.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/LoadBalancingClientListenerImpl.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/ServerEventListenerImpl.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/RequestStats.java rename {reactivesocket-core/src/main/java/io/reactivesocket => reactivesocket-transport-local/src/main/java/io/reactivesocket/local}/internal/ValidatingSubscription.java (98%) diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java index 9211e742d..e907762f8 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancer.java @@ -18,15 +18,9 @@ import io.reactivesocket.Availability; import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.client.events.LoadBalancingClientListener; -import io.reactivesocket.events.ClientEventListener; -import io.reactivesocket.events.EventSource; import io.reactivesocket.exceptions.NoAvailableReactiveSocketException; import io.reactivesocket.exceptions.TimeoutException; import io.reactivesocket.exceptions.TransportException; -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.internal.ValidatingSubscription; import io.reactivesocket.stat.Ewma; import io.reactivesocket.stat.FrugalQuantile; import io.reactivesocket.stat.Median; @@ -38,7 +32,12 @@ import org.reactivestreams.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import reactor.core.publisher.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSource; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; +import reactor.core.publisher.MonoSource; +import reactor.core.publisher.Operators; import java.nio.channels.ClosedChannelException; import java.util.ArrayList; @@ -88,8 +87,8 @@ public class LoadBalancer implements ReactiveSocket { private Runnable readyCallback; private int pendingSockets; - private final ActiveList activeSockets; - private final ActiveList activeFactories; + private final ArrayList activeSockets; + private final ArrayList activeFactories; private final FactoriesRefresher factoryRefresher; private final Mono selectSocket; @@ -99,10 +98,6 @@ public class LoadBalancer implements ReactiveSocket { private long refreshPeriod; private volatile long lastRefresh; private final MonoProcessor closeSubject = MonoProcessor.create(); - - private final LoadBalancingClientListener eventListener; - private final EventPublisher eventPublisher; - /** * * @param factories the source (factories) of ReactiveSocket @@ -132,23 +127,14 @@ public LoadBalancer( double maxPendings, int minAperture, int maxAperture, - long maxRefreshPeriodMs, - EventPublisher eventPublisher + long maxRefreshPeriodMs ) { this.expFactor = expFactor; this.lowerQuantile = new FrugalQuantile(lowQuantile); this.higherQuantile = new FrugalQuantile(highQuantile); - this.eventPublisher = eventPublisher; - if (eventPublisher.isEventPublishingEnabled() - && eventPublisher.getEventListener() instanceof LoadBalancingClientListener) { - eventListener = (LoadBalancingClientListener) eventPublisher.getEventListener(); - } else { - eventListener = null; - } - - this.activeSockets = new ActiveList<>(eventListener, false); - this.activeFactories = new ActiveList<>(eventListener, true); + this.activeSockets = new ArrayList<>(); + this.activeFactories = new ArrayList<>(); this.pendingSockets = 0; this.factoryRefresher = new FactoriesRefresher(); this.selectSocket = Mono.fromCallable(this::select); @@ -174,20 +160,17 @@ public LoadBalancer(Publisher> factor DEFAULT_LOWER_QUANTILE, DEFAULT_HIGHER_QUANTILE, DEFAULT_MIN_PENDING, DEFAULT_MAX_PENDING, DEFAULT_MIN_APERTURE, DEFAULT_MAX_APERTURE, - DEFAULT_MAX_REFRESH_PERIOD_MS, - new DisabledEventPublisher<>() + DEFAULT_MAX_REFRESH_PERIOD_MS ); } - LoadBalancer(Publisher> factories, Runnable readyCallback, - EventPublisher eventPublisher) { + LoadBalancer(Publisher> factories, Runnable readyCallback) { this(factories, DEFAULT_EXP_FACTOR, DEFAULT_LOWER_QUANTILE, DEFAULT_HIGHER_QUANTILE, DEFAULT_MIN_PENDING, DEFAULT_MAX_PENDING, DEFAULT_MIN_APERTURE, DEFAULT_MAX_APERTURE, - DEFAULT_MAX_REFRESH_PERIOD_MS, - eventPublisher + DEFAULT_MAX_REFRESH_PERIOD_MS ); this.readyCallback = readyCallback; } @@ -229,7 +212,7 @@ private synchronized void addSockets(int numberOfNewSocket) { while (n > 0) { int size = activeFactories.size(); if (size == 1) { - ReactiveSocketClient factory = activeFactories.holder.get(0); + ReactiveSocketClient factory = activeFactories.get(0); if (factory.availability() > 0.0) { activeFactories.remove(0); pendingSockets++; @@ -247,8 +230,8 @@ private synchronized void addSockets(int numberOfNewSocket) { if (i1 >= i0) { i1++; } - factory0 = activeFactories.holder.get(i0); - factory1 = activeFactories.holder.get(i1); + factory0 = activeFactories.get(i0); + factory1 = activeFactories.get(i1); if (factory0.availability() > 0.0 && factory1.availability() > 0.0) { break; } @@ -260,7 +243,7 @@ private synchronized void addSockets(int numberOfNewSocket) { // cheaper to permute activeFactories.get(i1) with the last item and remove the last // rather than doing a activeFactories.remove(i1) if (i1 < size - 1) { - activeFactories.set(i1, activeFactories.holder.get(size - 1)); + activeFactories.set(i1, activeFactories.get(size - 1)); } activeFactories.remove(size - 1); factory1.connect().subscribe(new SocketAdder(factory1)); @@ -269,7 +252,7 @@ private synchronized void addSockets(int numberOfNewSocket) { pendingSockets++; // c.f. above if (i0 < size - 1) { - activeFactories.set(i0, activeFactories.holder.get(size - 1)); + activeFactories.set(i0, activeFactories.get(size - 1)); } activeFactories.remove(size - 1); factory0.connect().subscribe(new SocketAdder(factory0)); @@ -284,7 +267,7 @@ private synchronized void refreshAperture() { } double p = 0.0; - for (WeightedSocket wrs: activeSockets.holder) { + for (WeightedSocket wrs: activeSockets) { p += wrs.getPending(); } p /= n + pendingSockets; @@ -315,9 +298,6 @@ private void updateAperture(int newValue, long now) { pendings.reset((minPendings + maxPendings)/2); if (targetAperture != previous) { - if (eventListener != null) { - eventListener.apertureChanged(previous, targetAperture); - } logger.debug("Current pending={}, new target={}, previous target={}", pendings.value(), targetAperture, previous); } @@ -332,7 +312,7 @@ private void updateAperture(int newValue, long now) { private synchronized void refreshSockets() { refreshAperture(); int n = pendingSockets + activeSockets.size(); - if (n < targetAperture && !activeFactories.holder.isEmpty()) { + if (n < targetAperture && !activeFactories.isEmpty()) { logger.debug("aperture {} is below target {}, adding {} sockets", n, targetAperture, targetAperture - n); addSockets(targetAperture - n); @@ -344,20 +324,11 @@ private synchronized void refreshSockets() { long now = Clock.now(); if (now - lastRefresh >= refreshPeriod) { - if (eventListener != null) { - eventListener.socketsRefreshStart(); - } long prev = refreshPeriod; refreshPeriod = (long) Math.min(refreshPeriod * 1.5, maxRefreshPeriod); logger.debug("Bumping refresh period, {}->{}", prev / 1000, refreshPeriod / 1000); - if (prev != refreshPeriod && eventListener != null) { - eventListener.socketRefreshPeriodChanged(prev, refreshPeriod, Clock.unit()); - } lastRefresh = now; addSockets(1); - if (eventListener != null) { - eventListener.socketsRefreshCompleted(Clock.elapsedSince(now), Clock.unit()); - } } } @@ -368,7 +339,7 @@ private synchronized void quickSlowestRS() { WeightedSocket slowest = null; double lowestAvailability = Double.MAX_VALUE; - for (WeightedSocket socket: activeSockets.holder) { + for (WeightedSocket socket: activeSockets) { double load = socket.availability(); if (load == 0.0) { slowest = socket; @@ -405,8 +376,8 @@ private synchronized void removeSocket(WeightedSocket socket, boolean refresh) { @Override public synchronized double availability() { double currentAvailability = 0.0; - if (!activeSockets.holder.isEmpty()) { - for (WeightedSocket rs : activeSockets.holder) { + if (!activeSockets.isEmpty()) { + for (WeightedSocket rs : activeSockets) { currentAvailability += rs.availability(); } currentAvailability /= activeSockets.size(); @@ -416,14 +387,14 @@ public synchronized double availability() { } private synchronized ReactiveSocket select() { - if (activeSockets.holder.isEmpty()) { + if (activeSockets.isEmpty()) { return FAILING_REACTIVE_SOCKET; } refreshSockets(); int size = activeSockets.size(); if (size == 1) { - return activeSockets.holder.get(0); + return activeSockets.get(0); } WeightedSocket rsc1 = null; @@ -436,12 +407,12 @@ private synchronized ReactiveSocket select() { if (i2 >= i1) { i2++; } - rsc1 = activeSockets.holder.get(i1); - rsc2 = activeSockets.holder.get(i2); + rsc1 = activeSockets.get(i1); + rsc2 = activeSockets.get(i2); if (rsc1.availability() > 0.0 && rsc2.availability() > 0.0) { break; } - if (i+1 == EFFORT && !activeFactories.holder.isEmpty()) { + if (i+1 == EFFORT && !activeFactories.isEmpty()) { addSockets(1); } } @@ -494,14 +465,14 @@ public synchronized String toString() { @Override public Mono close() { return MonoSource.wrap(subscriber -> { - subscriber.onSubscribe(ValidatingSubscription.empty(subscriber)); + subscriber.onSubscribe(Operators.emptySubscription()); synchronized (this) { factoryRefresher.close(); activeFactories.clear(); AtomicInteger n = new AtomicInteger(activeSockets.size()); - activeSockets.holder.forEach(rs -> { + activeSockets.forEach(rs -> { rs.close().subscribe(new Subscriber() { @Override public void onSubscribe(Subscription s) { @@ -554,8 +525,8 @@ public void onNext(Collection newFactories) { Set current = new HashSet<>(activeFactories.size() + activeSockets.size()); - current.addAll(activeFactories.holder); - for (WeightedSocket socket: activeSockets.holder) { + current.addAll(activeFactories); + for (WeightedSocket socket: activeSockets) { ReactiveSocketClient factory = socket.getFactory(); current.add(factory); } @@ -567,12 +538,11 @@ public void onNext(Collection newFactories) { added.removeAll(current); boolean changed = false; - Iterator it0 = activeSockets.holder.iterator(); + Iterator it0 = activeSockets.iterator(); while (it0.hasNext()) { WeightedSocket socket = it0.next(); if (removed.contains(socket.getFactory())) { it0.remove(); - activeSockets.publishRemoveEvent(socket); try { changed = true; socket.close(); @@ -581,12 +551,11 @@ public void onNext(Collection newFactories) { } } } - Iterator it1 = activeFactories.holder.iterator(); + Iterator it1 = activeFactories.iterator(); while (it1.hasNext()) { ReactiveSocketClient factory = it1.next(); if (removed.contains(factory)) { it1.remove(); - activeFactories.publishRemoveEvent(factory); changed = true; } } @@ -596,11 +565,11 @@ public void onNext(Collection newFactories) { if (changed && logger.isDebugEnabled()) { StringBuilder msgBuilder = new StringBuilder(); msgBuilder.append("\nUpdated active factories (size: " + activeFactories.size() + ")\n"); - for (ReactiveSocketClient f : activeFactories.holder) { + for (ReactiveSocketClient f : activeFactories) { msgBuilder.append(" + ").append(f).append('\n'); } msgBuilder.append("Active sockets:\n"); - for (WeightedSocket socket: activeSockets.holder) { + for (WeightedSocket socket: activeSockets) { msgBuilder.append(" + ").append(socket).append('\n'); } logger.debug(msgBuilder.toString()); @@ -651,9 +620,6 @@ public void onNext(ReactiveSocket rs) { logger.debug("Adding new WeightedSocket {}", weightedSocket); activeSockets.add(weightedSocket); - if (eventListener != null) { - eventListener.socketAdded(weightedSocket); - } if (readyCallback != null) { readyCallback.run(); } @@ -1047,60 +1013,37 @@ public void onComplete() { private class ActiveList { private final ArrayList holder; - private final LoadBalancingClientListener listener; private final boolean server; - public ActiveList(LoadBalancingClientListener listener, boolean server) { - this.listener = listener; + public ActiveList(boolean server) { this.server = server; - holder = new ArrayList(128); + holder = new ArrayList<>(128); } public void add(T item) { holder.add(item); - publishAddEvent(item); } public T remove(int index) { T item = holder.remove(index); - if (item != null) { - publishRemoveEvent(item); - } return item; } public boolean remove(T item) { boolean removed = holder.remove(item); - if (removed) { - publishRemoveEvent(item); - } return removed; } public T set(int index, T item) { T prev = holder.set(index, item); - if (prev != null) { - publishRemoveEvent(prev); - } - publishAddEvent(item); return prev; } public void addAll(Collection toAdd) { holder.addAll(toAdd); - if (listener != null) { - for (T t : toAdd) { - publishAddEvent(t); - } - } } public void clear() { - if (listener != null) { - for (T t : holder) { - publishRemoveEvent(t); - } - } holder.clear(); } @@ -1108,31 +1051,5 @@ public int size() { return holder.size(); } - private void publishRemoveEvent(T item) { - if (listener == null) { - return; - } - if (server) { - listener.serverRemoved(item); - } else { - listener.socketRemoved(item); - } - } - - private void publishAddEvent(T item) { - if (server && eventPublisher.isEventPublishingEnabled()) { - @SuppressWarnings("unchecked") - EventSource src = (EventSource) item; - src.subscribe(eventPublisher.getEventListener()); - } - if (listener == null) { - return; - } - if (server) { - listener.serverAdded(item); - } else { - listener.socketAdded(item); - } - } } } diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancerInitializer.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancerInitializer.java index 24b77e2b3..163fd2f0f 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancerInitializer.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancerInitializer.java @@ -17,8 +17,6 @@ package io.reactivesocket.client; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.events.AbstractEventSource; -import io.reactivesocket.events.ClientEventListener; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoProcessor; @@ -29,20 +27,20 @@ * This is a temporary class to provide a {@link LoadBalancingClient#connect()} implementation when {@link LoadBalancer} * does not support it. */ -final class LoadBalancerInitializer extends AbstractEventSource implements Runnable { +final class LoadBalancerInitializer implements ReactiveSocketClient, Runnable { private final LoadBalancer loadBalancer; private final MonoProcessor emitSource = MonoProcessor.create(); private LoadBalancerInitializer(Publisher> factories) { - loadBalancer = new LoadBalancer(factories, this, this); + loadBalancer = new LoadBalancer(factories, this); } static LoadBalancerInitializer create(Publisher> factories) { return new LoadBalancerInitializer(factories); } - Mono connect() { + public Mono connect() { return emitSource; } @@ -51,7 +49,7 @@ public void run() { emitSource.onNext(loadBalancer); } - synchronized double availability() { + public synchronized double availability() { return emitSource.isTerminated() ? 1.0 : 0.0; } } diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancingClient.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancingClient.java index b3acefaae..4f35e2ad6 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancingClient.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/LoadBalancingClient.java @@ -32,12 +32,11 @@ * An implementation of {@code ReactiveSocketClient} that operates on a cluster of target servers instead of a single * server. */ -public class LoadBalancingClient extends AbstractReactiveSocketClient { +public class LoadBalancingClient implements ReactiveSocketClient { private final LoadBalancerInitializer initializer; public LoadBalancingClient(LoadBalancerInitializer initializer) { - super(initializer); this.initializer = initializer; } @@ -65,7 +64,7 @@ public double availability() { */ public static LoadBalancingClient create(Publisher> servers, Function clientFactory) { - SourceToClient f = new SourceToClient(clientFactory); + SourceToClient f = new SourceToClient<>(clientFactory); return new LoadBalancingClient(LoadBalancerInitializer.create(Flux.from(servers).map(f))); } diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/events/LoadBalancingClientListener.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/events/LoadBalancingClientListener.java deleted file mode 100644 index 0d0946593..000000000 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/events/LoadBalancingClientListener.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.client.events; - -import io.reactivesocket.Availability; -import io.reactivesocket.client.LoadBalancingClient; -import io.reactivesocket.events.ClientEventListener; - -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -/** - * A {@link ClientEventListener} for {@link LoadBalancingClient} - */ -public interface LoadBalancingClientListener extends ClientEventListener { - - /** - * Event when a new socket is added to the load balancer. - * - * @param availability Availability for the added socket. - */ - default void socketAdded(Availability availability) {} - - /** - * Event when a socket is removed from the load balancer. - * - * @param availability Availability for the removed socket. - */ - default void socketRemoved(Availability availability) {} - - /** - * An event when a server is added to the load balancer. - * - * @param availability Availability of the added server. - */ - default void serverAdded(Availability availability) {} - - /** - * An event when a server is removed from the load balancer. - * - * @param availability Availability of the removed server. - */ - default void serverRemoved(Availability availability) {} - - /** - * An event when the expected number of active sockets held by the load balancer changes. - * - * @param oldAperture Old aperture size, i.e. expected number of active sockets. - * @param newAperture New aperture size, i.e. expected number of active sockets. - */ - default void apertureChanged(int oldAperture, int newAperture) {} - - /** - * An event when the expected time period for refreshing active sockets in the load balancer changes. - * - * @param oldPeriod Old refresh period. - * @param newPeriod New refresh period. - * @param periodUnit {@link TimeUnit} for the refresh period. - */ - default void socketRefreshPeriodChanged(long oldPeriod, long newPeriod, TimeUnit periodUnit) {} - - /** - * An event to mark the start of the socket refresh cycle. - */ - default void socketsRefreshStart() {} - - /** - * An event to mark the end of the socket refresh cycle. - * - * @param duration Time taken to refresh sockets. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void socketsRefreshCompleted(long duration, TimeUnit durationUnit) {} -} diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/events/LoggingLoadBalancingClientListener.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/events/LoggingLoadBalancingClientListener.java deleted file mode 100644 index fea51cea4..000000000 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/events/LoggingLoadBalancingClientListener.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.client.events; - -import io.reactivesocket.Availability; -import io.reactivesocket.events.LoggingClientEventListener; -import org.slf4j.event.Level; - -import java.util.concurrent.TimeUnit; - -public class LoggingLoadBalancingClientListener extends LoggingClientEventListener implements LoadBalancingClientListener { - - public LoggingLoadBalancingClientListener(String name, Level logLevel) { - super(name, logLevel); - } - - @Override - public void socketAdded(Availability availability) { - logIfEnabled(() -> name + ": socketAdded " + "availability = [" + availability + ']'); - } - - @Override - public void socketRemoved(Availability availability) { - logIfEnabled(() -> name + ": socketRemoved " + "availability = [" + availability + ']'); - } - - @Override - public void serverAdded(Availability availability) { - logIfEnabled(() -> name + ": serverAdded " + "availability = [" + availability + ']'); - } - - @Override - public void serverRemoved(Availability availability) { - logIfEnabled(() -> name + ": serverRemoved " + "availability = [" + availability + ']'); - } - - @Override - public void apertureChanged(int oldAperture, int newAperture) { - logIfEnabled(() -> name + ": apertureChanged " + "oldAperture = [" + oldAperture + "newAperture = [" - + newAperture + ']'); - } - - @Override - public void socketRefreshPeriodChanged(long oldPeriod, long newPeriod, TimeUnit periodUnit) { - logIfEnabled(() -> name + ": socketRefreshPeriodChanged " + "newPeriod = [" + newPeriod + "], periodUnit = [" - + periodUnit + ']'); - } - - @Override - public void socketsRefreshStart() { - logIfEnabled(() -> name + ": socketsRefreshStart"); - } - - @Override - public void socketsRefreshCompleted(long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": socketsRefreshCompleted " + "duration = [" + duration + - "], durationUnit = [" + durationUnit + ']'); - } -} diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/FailureAwareClient.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/FailureAwareClient.java index 59b60397c..4abe74fcf 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/FailureAwareClient.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/FailureAwareClient.java @@ -17,7 +17,6 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.client.AbstractReactiveSocketClient; import io.reactivesocket.client.ReactiveSocketClient; import io.reactivesocket.stat.Ewma; import io.reactivesocket.util.Clock; @@ -36,7 +35,7 @@ * lot of them when sending messages, we will still decrease the availability of the child * reducing the probability of connecting to it. */ -public class FailureAwareClient extends AbstractReactiveSocketClient { +public class FailureAwareClient implements ReactiveSocketClient { private static final double EPSILON = 1e-4; @@ -46,7 +45,6 @@ public class FailureAwareClient extends AbstractReactiveSocketClient { private final Ewma errorPercentage; public FailureAwareClient(ReactiveSocketClient delegate, long halfLife, TimeUnit unit) { - super(delegate); this.delegate = delegate; this.tau = Clock.unit().convert((long)(halfLife / Math.log(2)), unit); this.stamp = Clock.now(); diff --git a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSocketClients.java b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSocketClients.java index aba25ee38..2a034f129 100644 --- a/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSocketClients.java +++ b/reactivesocket-client/src/main/java/io/reactivesocket/client/filter/ReactiveSocketClients.java @@ -17,7 +17,6 @@ package io.reactivesocket.client.filter; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.client.AbstractReactiveSocketClient; import io.reactivesocket.client.ReactiveSocketClient; import reactor.core.publisher.Mono; @@ -43,7 +42,7 @@ private ReactiveSocketClients() { * @return New client that imposes the passed {@code timeout}. */ public static ReactiveSocketClient connectTimeout(ReactiveSocketClient orig, Duration timeout) { - return new AbstractReactiveSocketClient(orig) { + return new ReactiveSocketClient() { @Override public Mono connect() { return orig.connect().timeout(timeout); @@ -78,7 +77,7 @@ public static ReactiveSocketClient detectFailures(ReactiveSocketClient orig) { * @return A new client wrapping the original. */ public static ReactiveSocketClient wrap(ReactiveSocketClient orig, Function mapper) { - return new AbstractReactiveSocketClient(orig) { + return new ReactiveSocketClient() { @Override public Mono connect() { return orig.connect().map(mapper); diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java index ad1cbc595..178582bbb 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java @@ -115,7 +115,7 @@ private void testReactiveSocket(BiConsumer f) th throw new RuntimeException(); } }); - ReactiveSocketClient factory = new AbstractReactiveSocketClient() { + ReactiveSocketClient factory = new ReactiveSocketClient() { @Override public Mono connect() { return Mono.just(socket); diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java index e4b093432..318f89496 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java @@ -133,7 +133,7 @@ public void onComplete() { } private static ReactiveSocketClient succeedingFactory(ReactiveSocket socket) { - return new AbstractReactiveSocketClient() { + return new ReactiveSocketClient() { @Override public Mono connect() { return Mono.just(socket); @@ -148,7 +148,7 @@ public double availability() { } private static ReactiveSocketClient failingClient(SocketAddress sa) { - return new AbstractReactiveSocketClient() { + return new ReactiveSocketClient() { @Override public Mono connect() { Assert.fail(); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java index 0f554f23a..da45a2094 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java @@ -17,10 +17,7 @@ package io.reactivesocket; import io.reactivesocket.client.KeepAliveProvider; -import io.reactivesocket.events.EventListener; import io.reactivesocket.exceptions.Exceptions; -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; import io.reactivesocket.internal.KnownErrorFilter; import io.reactivesocket.internal.LimitableRequestPublisher; import io.reactivesocket.lease.Lease; @@ -56,8 +53,7 @@ public class ClientReactiveSocket implements ReactiveSocket { private volatile Consumer leaseConsumer; // Provided on start() public ClientReactiveSocket(DuplexConnection connection, Consumer errorConsumer, - StreamIdSupplier streamIdSupplier, KeepAliveProvider keepAliveProvider, - EventPublisher publisher) { + StreamIdSupplier streamIdSupplier, KeepAliveProvider keepAliveProvider) { this.connection = connection; this.errorConsumer = new KnownErrorFilter(errorConsumer); this.streamIdSupplier = streamIdSupplier; @@ -71,11 +67,6 @@ public ClientReactiveSocket(DuplexConnection connection, Consumer err .subscribe(); } - public ClientReactiveSocket(DuplexConnection connection, Consumer errorConsumer, - StreamIdSupplier streamIdSupplier, KeepAliveProvider keepAliveProvider) { - this(connection, errorConsumer, streamIdSupplier, keepAliveProvider, new DisabledEventPublisher<>()); - } - @Override public Mono fireAndForget(Payload payload) { Mono defer = Mono.defer(() -> { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index 14516ca5d..3597ff1e4 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -18,11 +18,8 @@ import io.reactivesocket.Frame.Lease; import io.reactivesocket.Frame.Request; -import io.reactivesocket.events.EventListener; import io.reactivesocket.exceptions.ApplicationException; import io.reactivesocket.frame.FrameHeaderFlyweight; -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; import io.reactivesocket.internal.KnownErrorFilter; import io.reactivesocket.internal.LimitableRequestPublisher; import io.reactivesocket.lease.LeaseEnforcingSocket; @@ -54,8 +51,7 @@ public class ServerReactiveSocket implements ReactiveSocket { private volatile Disposable subscribe; public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestHandler, - boolean clientHonorsLease, Consumer errorConsumer, - EventPublisher eventPublisher) { + boolean clientHonorsLease, Consumer errorConsumer) { this.requestHandler = requestHandler; this.connection = connection; this.errorConsumer = new KnownErrorFilter(errorConsumer); @@ -79,11 +75,6 @@ public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestH } } - public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestHandler, - boolean clientHonorsLease, Consumer errorConsumer) { - this(connection, requestHandler, clientHonorsLease, errorConsumer, new DisabledEventPublisher<>()); - } - public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestHandler, Consumer errorConsumer) { this(connection, requestHandler, true, errorConsumer); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket2.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket2.java deleted file mode 100644 index 183c0bc79..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket2.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket; - -import io.reactivesocket.Frame.Lease; -import io.reactivesocket.Frame.Request; -import io.reactivesocket.events.EventListener; -import io.reactivesocket.exceptions.ApplicationException; -import io.reactivesocket.frame.FrameHeaderFlyweight; -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.internal.KnownErrorFilter; -import io.reactivesocket.internal.LimitableRequestPublisher; -import io.reactivesocket.lease.LeaseEnforcingSocket; -import org.agrona.collections.Int2ObjectHashMap; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscription; -import reactor.core.Disposable; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.publisher.UnicastProcessor; - -import java.util.Collection; -import java.util.function.Consumer; - -/** - * Server side ReactiveSocket. Receives {@link Frame}s from a - * {@link ClientReactiveSocket} - */ -public class ServerReactiveSocket2 implements ReactiveSocket { - - private final DuplexConnection connection; - private final Consumer errorConsumer; - - private final Int2ObjectHashMap sendingSubscriptions; - private final Int2ObjectHashMap> receivers; - - private final ReactiveSocket requestHandler; - - private volatile Disposable subscribe; - - public ServerReactiveSocket2(DuplexConnection connection, ReactiveSocket requestHandler, - boolean clientHonorsLease, Consumer errorConsumer, - EventPublisher eventPublisher) { - this.requestHandler = requestHandler; - this.connection = connection; - this.errorConsumer = new KnownErrorFilter(errorConsumer); - this.sendingSubscriptions = new Int2ObjectHashMap<>(); - this.receivers = new Int2ObjectHashMap<>(); - - connection.onClose() - .doFinally(signalType -> cleanup()) - .subscribe(); - if (requestHandler instanceof LeaseEnforcingSocket) { - LeaseEnforcingSocket enforcer = (LeaseEnforcingSocket) requestHandler; - enforcer.acceptLeaseSender(lease -> { - if (!clientHonorsLease) { - return; - } - Frame leaseFrame = Lease.from(lease.getTtl(), lease.getAllowedRequests(), lease.metadata()); - connection.sendOne(leaseFrame) - .doOnError(errorConsumer) - .subscribe(); - }); - } - } - - public ServerReactiveSocket2(DuplexConnection connection, ReactiveSocket requestHandler, - boolean clientHonorsLease, Consumer errorConsumer) { - this(connection, requestHandler, clientHonorsLease, errorConsumer, new DisabledEventPublisher<>()); - } - - public ServerReactiveSocket2(DuplexConnection connection, ReactiveSocket requestHandler, - Consumer errorConsumer) { - this(connection, requestHandler, true, errorConsumer); - } - - @Override - public Mono fireAndForget(Payload payload) { - return requestHandler.fireAndForget(payload); - } - - @Override - public Mono requestResponse(Payload payload) { - return requestHandler.requestResponse(payload); - } - - @Override - public Flux requestStream(Payload payload) { - return requestHandler.requestStream(payload); - } - - @Override - public Flux requestChannel(Publisher payloads) { - return requestHandler.requestChannel(payloads); - } - - @Override - public Mono metadataPush(Payload payload) { - return requestHandler.metadataPush(payload); - } - - @Override - public Mono close() { - if (subscribe != null) { - subscribe.dispose(); - } - - return connection.close(); - } - - @Override - public Mono onClose() { - return connection.onClose(); - } - - public ServerReactiveSocket2 start() { - subscribe = connection - .receive() - .flatMap(frame -> { - int streamId = frame.getStreamId(); - UnicastProcessor receiver; - switch (frame.getType()) { - case FIRE_AND_FORGET: - return handleFireAndForget(streamId, fireAndForget(frame)); - case REQUEST_RESPONSE: - return handleRequestResponse(streamId, requestResponse(frame)); - case CANCEL: - return handleCancelFrame(streamId); - case KEEPALIVE: - return handleKeepAliveFrame(frame); - case REQUEST_N: - return handleRequestN(streamId, frame); - case REQUEST_STREAM: - return handleStream(streamId, requestStream(frame)); - case REQUEST_CHANNEL: - return handleChannel(streamId, frame); - case PAYLOAD: - // TODO: Hook in receiving socket. - return Mono.empty(); - case METADATA_PUSH: - return metadataPush(frame); - case LEASE: - // Lease must not be received here as this is the server end of the socket which sends leases. - return Mono.empty(); - case NEXT: - synchronized (ServerReactiveSocket2.this) { - receiver = receivers.get(streamId); - } - if (receiver != null) { - receiver.onNext(frame); - } - return Mono.empty(); - case COMPLETE: - synchronized (ServerReactiveSocket2.this) { - receiver = receivers.get(streamId); - } - if (receiver != null) { - receiver.onComplete(); - } - return Mono.empty(); - case ERROR: - synchronized (ServerReactiveSocket2.this) { - receiver = receivers.get(streamId); - } - if (receiver != null) { - receiver.onError(new ApplicationException(frame)); - } - return Mono.empty(); - case NEXT_COMPLETE: - synchronized (ServerReactiveSocket2.this) { - receiver = receivers.get(streamId); - } - if (receiver != null) { - receiver.onNext(frame); - receiver.onComplete(); - } - - return Mono.empty(); - - case SETUP: - return handleError(streamId, new IllegalStateException("Setup frame received post setup.")); - default: - return handleError(streamId, new IllegalStateException("ServerReactiveSocket: Unexpected frame type: " - + frame.getType())); - } - }) - .doOnError(t -> { - errorConsumer.accept(t); - - //TODO: This should be error? - - Collection values; - synchronized (this) { - values = sendingSubscriptions.values(); - } - values - .forEach(Subscription::cancel); - }) - .subscribe(); - return this; - } - - private synchronized void cleanup() { - subscribe.dispose(); - sendingSubscriptions.values().forEach(Subscription::cancel); - sendingSubscriptions.clear(); - receivers.values().forEach(Subscription::cancel); - sendingSubscriptions.clear(); - requestHandler.close().subscribe(); - } - - private Mono handleFireAndForget(int streamId, Mono result) { - return result - .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) - .doOnError(t -> { - removeSubscription(streamId); - errorConsumer.accept(t); - }) - .doFinally(signalType -> removeSubscription(streamId)) - .ignoreElement(); - } - - private Mono handleRequestResponse(int streamId, Mono response) { - Mono responseFrame = response - .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) - .map(payload -> - Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C)); - - return connection - .send(responseFrame) - .doOnError(throwable -> Frame.Error.from(streamId, throwable)) - .doOnCancel(() -> Frame.Cancel.from(streamId)) - .doFinally(signalType -> removeSubscription(streamId)) - .ignoreElement(); - } - - private Mono handleStream(int streamId, Flux response) { - return handleStream(streamId, response, 1); - } - - private Mono handleStream(int streamId, Flux response, int initialRequestN) { - Flux responseFrames = response - .map(payload -> Frame.PayloadFrame.from(streamId, FrameType.NEXT, payload)) - .onErrorResumeWith(throwable -> Mono.just(Frame.Error.from(streamId, throwable))) - .transform(f -> { - LimitableRequestPublisher wrap = LimitableRequestPublisher.wrap(f); - synchronized (ServerReactiveSocket2.this) { - wrap.increaseRequestLimit(initialRequestN); - sendingSubscriptions.put(streamId, wrap); - } - - return wrap; - }); - - return connection - .send(responseFrames) - .doOnError(throwable -> Frame.Error.from(streamId, throwable)) - .doOnCancel(() -> Frame.Cancel.from(streamId)) - .doFinally(signalType -> removeSubscription(streamId)) - .ignoreElement(); - - } - - private Mono handleChannel(int streamId, Frame firstFrame) { - return Mono.defer(() -> { - UnicastProcessor frames = UnicastProcessor.create(); - int initialRequestN = Request.initialRequestN(firstFrame); - - Flux payloads = frames - .doOnCancel(() -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); - } - }) - .doOnError(t -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); - } - }) - .doOnRequest(l -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.RequestN.from(streamId, l)).subscribe(null, errorConsumer::accept); - } - }) - .cast(Payload.class); - - Flux responses = requestChannel(payloads); - - return handleStream(streamId, responses, initialRequestN) - .doFinally(s -> { - synchronized (ServerReactiveSocket2.this) { - receivers.remove(streamId); - } - }); - }); - } - - private Mono handleKeepAliveFrame(Frame frame) { - if (Frame.Keepalive.hasRespondFlag(frame)) { - return connection.sendOne(Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, false)) - .doOnError(errorConsumer); - } - return Mono.empty(); - } - - private Mono handleCancelFrame(int streamId) { - Subscription subscription; - synchronized (this) { - subscription = sendingSubscriptions.remove(streamId); - } - - if (subscription != null) { - subscription.cancel(); - } - - return Mono.empty(); - } - - private Mono handleError(int streamId, Throwable t) { - errorConsumer.accept(t); - return connection - .sendOne(Frame.Error.from(streamId, t)) - .doOnError(errorConsumer); - } - - private Mono handleRequestN(int streamId, Frame frame) { - Subscription subscription; - synchronized (this) { - subscription = sendingSubscriptions.get(streamId); - } - if (subscription != null) { - int n = Frame.RequestN.requestN(frame); - subscription.request(n >= Integer.MAX_VALUE ? Long.MAX_VALUE : n); - } - return Mono.empty(); - } - - private synchronized void addSubscription(int streamId, Subscription subscription) { - sendingSubscriptions.put(streamId, subscription); - } - - private synchronized void removeSubscription(int streamId) { - sendingSubscriptions.remove(streamId); - } - -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/AbstractReactiveSocketClient.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/AbstractReactiveSocketClient.java deleted file mode 100644 index 777bbc295..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/AbstractReactiveSocketClient.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.client; - -import io.reactivesocket.events.AbstractEventSource; -import io.reactivesocket.events.ClientEventListener; -import io.reactivesocket.events.EventSource; - -public abstract class AbstractReactiveSocketClient extends AbstractEventSource - implements ReactiveSocketClient{ - - protected AbstractReactiveSocketClient() { - } - - protected AbstractReactiveSocketClient(EventSource delegate) { - super(delegate); - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java index e6e53aa75..a0db5efa0 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java @@ -24,13 +24,12 @@ * Default implementation of {@link ReactiveSocketClient} providing the functionality to create a {@link ReactiveSocket} * from a {@link TransportClient}. */ -public final class DefaultReactiveSocketClient extends AbstractReactiveSocketClient { +public final class DefaultReactiveSocketClient implements ReactiveSocketClient { private final Mono connectSource; public DefaultReactiveSocketClient(TransportClient transportClient, SetupProvider setupProvider, SocketAcceptor acceptor) { - super(setupProvider); connectSource = transportClient.connect() .then(connection -> setupProvider.accept(connection, acceptor)); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/ReactiveSocketClient.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/ReactiveSocketClient.java index 8c20d5c03..259b42573 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/ReactiveSocketClient.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/ReactiveSocketClient.java @@ -19,14 +19,12 @@ import io.reactivesocket.AbstractReactiveSocket; import io.reactivesocket.Availability; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.events.ClientEventListener; -import io.reactivesocket.events.EventSource; import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.transport.TransportClient; import reactor.core.publisher.Mono; -public interface ReactiveSocketClient extends Availability, EventSource { +public interface ReactiveSocketClient extends Availability { /** * Creates a new {@code ReactiveSocket} every time the returned {@code Publisher} is subscribed. diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProvider.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProvider.java index 5658d8692..5bc129e85 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProvider.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProvider.java @@ -16,18 +16,16 @@ package io.reactivesocket.client; -import io.reactivesocket.Payload; -import io.reactivesocket.client.ReactiveSocketClient.SocketAcceptor; -import io.reactivesocket.events.ClientEventListener; -import io.reactivesocket.events.EventSource; -import io.reactivesocket.lease.DefaultLeaseHonoringSocket; import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; import io.reactivesocket.Frame.Setup; -import io.reactivesocket.lease.DisableLeaseSocket; -import io.reactivesocket.lease.LeaseHonoringSocket; +import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.client.ReactiveSocketClient.SocketAcceptor; import io.reactivesocket.frame.SetupFrameFlyweight; +import io.reactivesocket.lease.DefaultLeaseHonoringSocket; +import io.reactivesocket.lease.DisableLeaseSocket; +import io.reactivesocket.lease.LeaseHonoringSocket; import io.reactivesocket.util.PayloadImpl; import reactor.core.publisher.Mono; @@ -36,7 +34,7 @@ /** * A provider for ReactiveSocket setup from a client. */ -public interface SetupProvider extends EventSource { +public interface SetupProvider { int DEFAULT_FLAGS = SetupFrameFlyweight.FLAGS_WILL_HONOR_LEASE | SetupFrameFlyweight.FLAGS_STRICT_INTERPRETATION; int DEFAULT_MAX_KEEP_ALIVE_MISSING_ACK = 3; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java index 5012bbd2c..d8eeb26e0 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java @@ -22,30 +22,26 @@ import io.reactivesocket.Frame; import io.reactivesocket.FrameType; import io.reactivesocket.Payload; +import io.reactivesocket.ReactiveSocket; import io.reactivesocket.ServerReactiveSocket; +import io.reactivesocket.StreamIdSupplier; import io.reactivesocket.client.ReactiveSocketClient.SocketAcceptor; -import io.reactivesocket.events.AbstractEventSource; -import io.reactivesocket.events.ClientEventListener; -import io.reactivesocket.events.ConnectionEventInterceptor; import io.reactivesocket.internal.ClientServerInputMultiplexer; -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; import io.reactivesocket.lease.DisableLeaseSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.lease.LeaseHonoringSocket; -import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.StreamIdSupplier; -import io.reactivesocket.util.Clock; import io.reactivesocket.util.PayloadImpl; import reactor.core.publisher.Mono; import java.util.function.Consumer; import java.util.function.Function; -import static io.reactivesocket.Frame.Setup.*; -import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static io.reactivesocket.Frame.Setup.from; +import static io.reactivesocket.Frame.Setup.getFlags; +import static io.reactivesocket.Frame.Setup.keepaliveInterval; +import static io.reactivesocket.Frame.Setup.maxLifetime; -final class SetupProviderImpl extends AbstractEventSource implements SetupProvider { +final class SetupProviderImpl implements SetupProvider { private final Frame setupFrame; private final Function leaseDecorator; @@ -63,34 +59,39 @@ final class SetupProviderImpl extends AbstractEventSource i @Override public Mono accept(DuplexConnection connection, SocketAcceptor acceptor) { - if (isEventPublishingEnabled()) { - ConnectionEventInterceptor interceptor = new ConnectionEventInterceptor(connection, this); - Mono source = _setup(interceptor, acceptor); - return Mono.using( - () -> new ConnectInspector(this), - connectInspector -> source - .doOnSuccess(connectInspector::connectSuccess) - .doOnError(connectInspector::connectFailed) - .doOnCancel(connectInspector::connectCancelled), - connectInspector -> {} - ); - } else { - return _setup(connection, acceptor); - } + return connection.sendOne(copySetupFrame()) + .then(() -> { + ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(connection); + ClientReactiveSocket sendingSocket = + new ClientReactiveSocket(multiplexer.asClientConnection(), errorConsumer, + StreamIdSupplier.clientSupplier(), + keepAliveProvider); + LeaseHonoringSocket leaseHonoringSocket = leaseDecorator.apply(sendingSocket); + + sendingSocket.start(leaseHonoringSocket); + + LeaseEnforcingSocket acceptingSocket = acceptor.accept(sendingSocket); + ServerReactiveSocket receivingSocket = new ServerReactiveSocket(multiplexer.asServerConnection(), + acceptingSocket, true, + errorConsumer); + receivingSocket.start(); + + return Mono.just(leaseHonoringSocket); + }); } @Override public SetupProvider dataMimeType(String dataMimeType) { Frame newSetup = from(getFlags(setupFrame), keepaliveInterval(setupFrame), maxLifetime(setupFrame), - Frame.Setup.metadataMimeType(setupFrame), dataMimeType, setupFrame); + Frame.Setup.metadataMimeType(setupFrame), dataMimeType, setupFrame); return new SetupProviderImpl(newSetup, leaseDecorator, keepAliveProvider, errorConsumer); } @Override public SetupProvider metadataMimeType(String metadataMimeType) { Frame newSetup = from(getFlags(setupFrame), keepaliveInterval(setupFrame), maxLifetime(setupFrame), - metadataMimeType, Frame.Setup.dataMimeType(setupFrame), - setupFrame); + metadataMimeType, Frame.Setup.dataMimeType(setupFrame), + setupFrame); return new SetupProviderImpl(newSetup, leaseDecorator, keepAliveProvider, errorConsumer); } @@ -107,90 +108,26 @@ public SetupProvider disableLease() { @Override public SetupProvider disableLease(Function socketFactory) { Frame newSetup = from(getFlags(setupFrame) & ~ConnectionSetupPayload.HONOR_LEASE, - keepaliveInterval(setupFrame), maxLifetime(setupFrame), - Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), - setupFrame); + keepaliveInterval(setupFrame), maxLifetime(setupFrame), + Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), + setupFrame); return new SetupProviderImpl(newSetup, socketFactory, keepAliveProvider, errorConsumer); } @Override public SetupProvider setupPayload(Payload setupPayload) { Frame newSetup = from(getFlags(setupFrame) & ~ConnectionSetupPayload.HONOR_LEASE, - keepaliveInterval(setupFrame), maxLifetime(setupFrame), - Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), - setupPayload); + keepaliveInterval(setupFrame), maxLifetime(setupFrame), + Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), + setupPayload); return new SetupProviderImpl(newSetup, reactiveSocket -> new DisableLeaseSocket(reactiveSocket), - keepAliveProvider, errorConsumer); + keepAliveProvider, errorConsumer); } private Frame copySetupFrame() { Frame newSetup = from(getFlags(setupFrame), keepaliveInterval(setupFrame), maxLifetime(setupFrame), - Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), - new PayloadImpl(setupFrame.getData().duplicate(), setupFrame.getMetadata().duplicate())); + Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), + new PayloadImpl(setupFrame.getData().duplicate(), setupFrame.getMetadata().duplicate())); return newSetup; } - - private Mono _setup(DuplexConnection connection, SocketAcceptor acceptor) { - return connection.sendOne(copySetupFrame()) - .then(() -> { - ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(connection); - ClientReactiveSocket sendingSocket = - new ClientReactiveSocket(multiplexer.asClientConnection(), errorConsumer, - StreamIdSupplier.clientSupplier(), - keepAliveProvider, this); - LeaseHonoringSocket leaseHonoringSocket = leaseDecorator.apply(sendingSocket); - - sendingSocket.start(leaseHonoringSocket); - - LeaseEnforcingSocket acceptingSocket = acceptor.accept(sendingSocket); - ServerReactiveSocket receivingSocket = new ServerReactiveSocket(multiplexer.asServerConnection(), - acceptingSocket, true, - errorConsumer, this); - receivingSocket.start(); - - return Mono.just(leaseHonoringSocket); - }); - } - - private static class ConnectInspector { - - private static final ConnectInspector empty = new ConnectInspector(new DisabledEventPublisher<>()); - private final EventPublisher publisher; - private final long startTime; - - public ConnectInspector(EventPublisher publisher) { - this.publisher = publisher; - startTime = Clock.now(); - if (publisher.isEventPublishingEnabled()) { - publisher.getEventListener().connectStart(); - } - } - - public void connectSuccess(ReactiveSocket socket) { - if (publisher.isEventPublishingEnabled()) { - publisher.getEventListener() - .connectCompleted(socket::availability, System.nanoTime() - startTime, NANOSECONDS); - socket.onClose() - .doFinally(signalType -> { - if (publisher.isEventPublishingEnabled()) { - publisher.getEventListener() - .socketClosed(Clock.elapsedSince(startTime), Clock.unit()); - } - }) - .subscribe(); - } - } - - public void connectFailed(Throwable cause) { - if (publisher.isEventPublishingEnabled()) { - publisher.getEventListener().connectFailed(System.nanoTime() - startTime, NANOSECONDS, cause); - } - } - - public void connectCancelled() { - if (publisher.isEventPublishingEnabled()) { - publisher.getEventListener().connectCancelled(System.nanoTime() - startTime, NANOSECONDS); - } - } - } } \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/AbstractEventSource.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/AbstractEventSource.java deleted file mode 100644 index 79522a9a1..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/AbstractEventSource.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import io.reactivesocket.internal.DisabledEventPublisher; -import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.internal.EventPublisherImpl; - -public abstract class AbstractEventSource implements EventSource, EventPublisher { - - private final EventSource delegate; - private volatile EventPublisher eventPublisher; - - protected AbstractEventSource() { - eventPublisher = new DisabledEventPublisher<>(); - delegate = new DisabledEventSource<>(); - } - - protected AbstractEventSource(EventSource delegate) { - this.delegate = delegate; - } - - @Override - public boolean isEventPublishingEnabled() { - return eventPublisher.isEventPublishingEnabled(); - } - - @Override - public EventSubscription subscribe(T listener) { - EventPublisher oldPublisher = null; - synchronized (this) { - if (eventPublisher != null) { - oldPublisher = eventPublisher; - } - eventPublisher = new EventPublisherImpl<>(listener); - } - EventSubscription delegateSubscription = delegate.subscribe(listener); - if (oldPublisher != null) { - // Dispose old listener and use the new one. - oldPublisher.cancel(); - } - return new EventSubscription() { - @Override - public void cancel() { - eventPublisher.cancel(); - delegateSubscription.cancel(); - synchronized (AbstractEventSource.this) { - if (eventPublisher == listener) { - eventPublisher = null; - } - } - } - }; - } - - @Override - public T getEventListener() { - return eventPublisher.getEventListener(); - } - - @Override - public void cancel() { - eventPublisher.cancel(); - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/ClientEventListener.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/ClientEventListener.java deleted file mode 100644 index 22d57ee36..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/ClientEventListener.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import java.util.concurrent.TimeUnit; -import java.util.function.DoubleSupplier; - -/** - * {@link EventListener} for a client. - */ -public interface ClientEventListener extends EventListener { - - /** - * Event when a new connection is initiated. - */ - default void connectStart() {} - - /** - * Event when a connection is successfully completed. - * - * @param socketAvailabilitySupplier A supplier for the availability of the connected socket. - * @param duration Time taken since connection initiation and completion. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void connectCompleted(DoubleSupplier socketAvailabilitySupplier, long duration, TimeUnit durationUnit) {} - - /** - * Event when a connection attempt fails. - * - * @param duration Time taken since connection initiation and failure. - * @param durationUnit {@code TimeUnit} for the duration. - * @param cause Cause for the failure. - */ - default void connectFailed(long duration, TimeUnit durationUnit, Throwable cause) {} - - /** - * Event when a connection attempt is cancelled. - * - * @param duration Time taken since connection initiation and cancellation. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void connectCancelled(long duration, TimeUnit durationUnit) {} -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/ConnectionEventInterceptor.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/ConnectionEventInterceptor.java deleted file mode 100644 index 22e42842c..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/ConnectionEventInterceptor.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import io.reactivesocket.DuplexConnection; -import io.reactivesocket.Frame; -import io.reactivesocket.internal.EventPublisher; -import org.reactivestreams.Publisher; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -public final class ConnectionEventInterceptor implements DuplexConnection { - - private static final Logger logger = LoggerFactory.getLogger(ConnectionEventInterceptor.class); - - private final DuplexConnection delegate; - private final EventPublisher publisher; - - public ConnectionEventInterceptor(DuplexConnection delegate, EventPublisher publisher) { - this.delegate = delegate; - this.publisher = publisher; - } - - @Override - public Mono send(Publisher frame) { - return delegate.send(Flux.from(frame).doOnNext(this::publishEventsForFrameWrite)); - } - - @Override - public Mono sendOne(Frame frame) { - return delegate.sendOne(frame); - } - - @Override - public Flux receive() { - return delegate.receive().doOnNext(this::publishEventsForFrameRead); - } - - @Override - public double availability() { - return delegate.availability(); - } - - @Override - public Mono close() { - return delegate.close(); - } - - @Override - public Mono onClose() { - return delegate.onClose(); - } - - private void publishEventsForFrameRead(Frame frameRead) { - if (!publisher.isEventPublishingEnabled()) { - return; - } - final EventListener listener = publisher.getEventListener(); - listener.frameRead(frameRead.getStreamId(), frameRead.getType()); - - switch (frameRead.getType()) { - case LEASE: - listener.leaseReceived(Frame.Lease.numberOfRequests(frameRead), Frame.Lease.ttl(frameRead)); - break; - case ERROR: - listener.errorReceived(frameRead.getStreamId(), Frame.Error.errorCode(frameRead)); - break; - } - } - - private void publishEventsForFrameWrite(Frame frameWritten) { - if (!publisher.isEventPublishingEnabled()) { - return; - } - final EventListener listener = publisher.getEventListener(); - listener.frameWritten(frameWritten.getStreamId(), frameWritten.getType()); - - switch (frameWritten.getType()) { - case LEASE: - listener.leaseSent(Frame.Lease.numberOfRequests(frameWritten), Frame.Lease.ttl(frameWritten)); - break; - case ERROR: - listener.errorSent(frameWritten.getStreamId(), Frame.Error.errorCode(frameWritten)); - break; - } - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/DisabledEventSource.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/DisabledEventSource.java deleted file mode 100644 index 9e44952bf..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/DisabledEventSource.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -public class DisabledEventSource implements EventSource { - - @Override - public EventSubscription subscribe(T listener) { - return EmptySubscription.INSTANCE; - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EmptySubscription.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EmptySubscription.java deleted file mode 100644 index a4397752d..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EmptySubscription.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import io.reactivesocket.events.EventSource.EventSubscription; - -public final class EmptySubscription implements EventSubscription { - - public static final EmptySubscription INSTANCE = new EmptySubscription(); - - private EmptySubscription() { - // No instances. - } - - @Override - public void cancel() { - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java deleted file mode 100644 index 6a9e1f216..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventListener.java +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import io.reactivesocket.DuplexConnection; -import io.reactivesocket.FrameType; -import io.reactivesocket.Payload; -import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.events.EventSource.EventSubscription; - -import java.util.concurrent.TimeUnit; - -/** - * A listener of events for {@link ReactiveSocket} - */ -public interface EventListener { - - /** - * An enum to represent the various interaction models of {@code ReactiveSocket}. - */ - enum RequestType { - REQUEST_RESPONSE, - REQUEST_STREAM, - REQUEST_CHANNEL, - METADATA_PUSH, - FIRE_AND_FORGET; - - public static RequestType fromFrameType(FrameType frameType) { - switch (frameType) { - case REQUEST_RESPONSE: - return REQUEST_RESPONSE; - case FIRE_AND_FORGET: - return FIRE_AND_FORGET; - case REQUEST_STREAM: - return REQUEST_STREAM; - case REQUEST_CHANNEL: - return REQUEST_CHANNEL; - case METADATA_PUSH: - return METADATA_PUSH; - default: - throw new IllegalArgumentException("Unknown frame type: " + frameType); - } - } - } - - /** - * Start event for receiving a new request from the peer. This callback will be invoked when the first frame for the - * request is received. - * - * @param streamId Stream Id for the request. - * @param type Request type. - */ - default void requestReceiveStart(int streamId, RequestType type) {} - - /** - * End event for receiving a new request from the peer. This callback will be invoked when the last frame for the - * request is received. For single item requests like {@link ReactiveSocket#requestResponse(Payload)}, the two - * events {@link #requestReceiveStart(int, RequestType)} and this will be emitted for the same frame. In case - * request ends with an error, {@link #requestReceiveFailed(int, RequestType, long, TimeUnit, Throwable)} will be - * called instead. - * - * @param streamId Stream Id for the request. - * @param type Request type. - * @param duration Time in the {@code durationUnit} since the start of the request receive. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void requestReceiveComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) {} - - /** - * End event for receiving a new request from the peer. This callback will be invoked when an error frame is - * received for the request. If the request is successfully completed, - * {@link #requestReceiveComplete(int, RequestType, long, TimeUnit)} will be called instead. - * - * @param streamId Stream Id for the request. - * @param type Request type. - * @param duration Time in the {@code durationUnit} since the start of the request receive. - * @param durationUnit {@code TimeUnit} for the duration. - * @param cause Cause for the failure. - */ - default void requestReceiveFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) {} - - /** - * Cancel event for receiving a new request from the peer. This callback will be invoked when request receive is - * cancelled. - * - * @param streamId Stream Id for the request. - * @param type Request type. - * @param duration Time in the {@code durationUnit} since the start of the request receive. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void requestReceiveCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) {} - - /** - * Start event for sending a new request to the peer. This callback will be invoked when first frame of the - * request is successfully written to the underlying {@link DuplexConnection}.

    - * For latencies related to write and buffering of frames, the events must be exposed by the transport. - * - * @param streamId Stream Id for the request. - * @param type Request type. - */ - default void requestSendStart(int streamId, RequestType type) {} - - /** - * End event for sending a new request to the peer. This callback will be invoked when last frame of the - * request is successfully written to the underlying {@link DuplexConnection}. - * - * @param streamId Stream Id for the request. - * @param type Request type. - * @param duration Time between subscription to request stream and last. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void requestSendComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) {} - - /** - * End event for sending a new request to the peer. This callback will be invoked if the request itself emits an - * error or the write to the underlying {@link DuplexConnection} failed. - * - * @param streamId Stream Id for the request. - * @param type Request type. - * @param duration Time between subscription to request stream and error. - * @param durationUnit {@code TimeUnit} for the duration. - * @param cause Cause for the failure. - */ - default void requestSendFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) {} - - /** - * Cancel event for sending a new request to the peer. This callback will be invoked if the write was cancelled by - * transport or user cancelled the response before the request was written. - * - * @param streamId Stream Id for the request. - * @param type Request type. - * @param duration Time between subscription to request stream and cancellation. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void requestSendCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - } - - /** - * Start event for sending a response to the peer. This callback will be invoked when first frame of the - * response is written to the underlying {@link DuplexConnection}. - * - * @param streamId Stream Id for the response. - * @param type Request type. - * @param duration Time between event {@link #requestReceiveComplete(int, RequestType, long, TimeUnit)} and this. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void responseSendStart(int streamId, RequestType type, long duration, TimeUnit durationUnit) {} - - /** - * End event for sending a response to the peer. This callback will be invoked when last frame of the - * response is written to the underlying {@link DuplexConnection}. - * - * @param streamId Stream Id for the response. - * @param type Request type. - * @param duration Time between subscription to response stream and last. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void responseSendComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) {} - - /** - * End event for sending a response to the peer. This callback will be invoked when the response terminates with - * an error. - * - * @param streamId Stream Id for the response. - * @param type Request type. - * @param duration Time between subscription to response stream and error. - * @param durationUnit {@code TimeUnit} for the duration. - * @param cause Cause for the failure. - */ - default void responseSendFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) {} - - /** - * Cancel event for sending a response to the peer. This callback will be invoked if the write was cancelled by - * transport or peer cancelled the response subscription. - * - * @param streamId Stream Id for the response. - * @param type Request type. - * @param duration Time between subscription to response stream and cancel. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void responseSendCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - } - - /** - * Start event for receiving a response from the peer. This callback will be invoked when first frame of the - * response is received from the underlying {@link DuplexConnection}. - * - * @param streamId Stream Id for the response. - * @param type Request type. - * @param duration Time between event {@link #requestSendComplete(int, RequestType, long, TimeUnit)} and this. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void responseReceiveStart(int streamId, RequestType type, long duration, TimeUnit durationUnit) {} - - /** - * End event for receiving a response from the peer. This callback will be invoked when last frame of the - * response is received from the underlying {@link DuplexConnection}. - * - * @param streamId Stream Id for the response. - * @param type Request type. - * @param duration Time between subscription to response stream and completion. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void responseReceiveComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) {} - - /** - * End event for receiving a response from the peer. This callback will be invoked when the response terminates with - * an error. - * - * @param streamId Stream Id for the response. - * @param type Request type. - * @param duration Time between subscription to response stream and error. - * @param durationUnit {@code TimeUnit} for the duration. - * @param cause Cause for the failure. - */ - default void responseReceiveFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) {} - - /** - * Cancel event for receiving a response from the peer. This callback will be invoked if the user cancelled the - * response subscription. - * - * @param streamId Stream Id for the response. - * @param type Request type. - * @param duration Time between subscription to response stream and error. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void responseReceiveCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - } - - /** - * On {@code ReactiveSocket} close. - * - * @param duration Time for which the socket was active. - * @param durationUnit {@code TimeUnit} for the duration. - */ - default void socketClosed(long duration, TimeUnit durationUnit) {} - - /** - * When a frame of type {@code frameType} is written. - * - * @param streamId Stream Id for the frame. - * @param frameType Type of the frame. - */ - default void frameWritten(int streamId, FrameType frameType) {} - - /** - * When a frame of type {@code frameType} is read. - * - * @param streamId Stream Id for the frame. - * @param frameType Type of the frame. - */ - default void frameRead(int streamId, FrameType frameType) {} - - /** - * When a lease is sent. - * - * @param permits Permits in the lease. - * @param ttl Time to live for the lease. - */ - default void leaseSent(int permits, int ttl) {} - - /** - * When a lease is received. - * - * @param permits Permits in the lease. - * @param ttl Time to live for the lease. - */ - default void leaseReceived(int permits, int ttl) {} - - /** - * When an error is sent. - * - * @param streamId Stream Id for the error. - * @param errorCode Error code. - */ - default void errorSent(int streamId, int errorCode) {} - - /** - * When an error is received. - * - * @param streamId Stream Id for the error. - * @param errorCode Error code. - */ - default void errorReceived(int streamId, int errorCode) {} - - /** - * Disposes this listener. This is a callback that can be invoked as a result of {@link EventSubscription#cancel()} - * of the associated subscription OR as an explicit signal from the {@link EventSource}.

    - * This would mark the end of notifications to this listener. Some in-flight notifications may be sent, due to - * the concurrent nature of the act of disposing and generation of notifications. - */ - default void dispose() { } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocket.java deleted file mode 100644 index 9ec014e87..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocket.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import io.reactivesocket.events.EventListener.RequestType; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -public interface EventPublishingSocket { - - EventPublishingSocket DISABLED = new EventPublishingSocket() { - @Override - public Mono decorateReceive(int streamId, Mono stream, RequestType requestType) { - return stream; - } - - @Override - public Flux decorateReceive(int streamId, Flux stream, RequestType requestType) { - return stream; - } - - @Override - public Mono decorateSend(int streamId, Mono stream, long receiveStartTimeNanos, - RequestType requestType) { - return stream; - } - }; - - Mono decorateReceive(int streamId, Mono stream, RequestType requestType); - - Flux decorateReceive(int streamId, Flux stream, RequestType requestType); - - Mono decorateSend(int streamId, Mono stream, long receiveStartTimeNanos, - RequestType requestType); - -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocketImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocketImpl.java deleted file mode 100644 index ba2dd3b9b..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventPublishingSocketImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import io.reactivesocket.events.EventListener.RequestType; -import io.reactivesocket.internal.EventPublisher; -import io.reactivesocket.util.Clock; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.concurrent.TimeUnit; - -public class EventPublishingSocketImpl implements EventPublishingSocket { - - private final EventPublisher eventPublisher; - private final boolean client; - - public EventPublishingSocketImpl(EventPublisher eventPublisher, boolean client) { - this.eventPublisher = eventPublisher; - this.client = client; - } - - @Override - public Mono decorateReceive(int streamId, Mono stream, RequestType requestType) { - return Mono.using( - () -> new ReceiveInterceptor(streamId, requestType, Clock.now()), - receiveInterceptor -> stream - .doOnSuccess(t -> receiveInterceptor.receiveComplete()) - .doOnError(receiveInterceptor::receiveFailed) - .doOnCancel(receiveInterceptor::receiveCancelled), - receiveInterceptor -> {} - ); - } - - @Override - public Flux decorateReceive(int streamId, Flux stream, RequestType requestType) { - return Flux.using( - () -> new ReceiveInterceptor(streamId, requestType, Clock.now()), - receiveInterceptor -> stream - .doOnComplete(receiveInterceptor::receiveComplete) - .doOnError(receiveInterceptor::receiveFailed) - .doOnCancel(receiveInterceptor::receiveCancelled), - receiveInterceptor -> {} - ); - } - - @Override - public Mono decorateSend(int streamId, Mono stream, long receiveStartTimeNanos, RequestType requestType) { - return Mono.using( - () -> new SendInterceptor(streamId, requestType, receiveStartTimeNanos), - sendInterceptor -> stream - .doOnSuccess(t -> sendInterceptor.sendComplete()) - .doOnError(sendInterceptor::sendFailed) - .doOnCancel(sendInterceptor::sendCancelled), - sendInterceptor -> {} - ); - } - - private class ReceiveInterceptor { - - private final long startTime; - private final RequestType requestType; - private final int streamId; - - public ReceiveInterceptor(int streamId, RequestType requestType, long startTime) { - this.streamId = streamId; - this.startTime = startTime; - this.requestType = requestType; - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - if (client) { - eventListener.responseReceiveStart(streamId, requestType, Clock.elapsedSince(startTime), - Clock.unit()); - } else { - eventListener.requestReceiveStart(streamId, requestType); - } - } - } - - public void receiveComplete() { - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - if (client) { - eventListener - .responseReceiveComplete(streamId, requestType, Clock.elapsedSince(startTime), - Clock.unit()); - } else { - eventListener - .requestReceiveComplete(streamId, requestType, Clock.elapsedSince(startTime), Clock.unit()); - } - } - } - - public void receiveFailed(Throwable cause) { - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - if (client) { - eventListener.responseReceiveFailed(streamId, requestType, - Clock.elapsedSince(startTime), Clock.unit(), cause); - } else { - eventListener.requestReceiveFailed(streamId, requestType, - Clock.elapsedSince(startTime), Clock.unit(), cause); - } - } - } - - public void receiveCancelled() { - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - if (client) { - eventListener.responseReceiveCancelled(streamId, requestType, - Clock.elapsedSince(startTime), Clock.unit()); - } else { - eventListener.requestReceiveCancelled(streamId, requestType, - Clock.elapsedSince(startTime), Clock.unit()); - } - } - } - } - - private class SendInterceptor { - - private final long startTime; - private final RequestType requestType; - private final int streamId; - - public SendInterceptor(int streamId, RequestType requestType, long receiveStartTimeNanos) { - this.streamId = streamId; - startTime = Clock.now(); - this.requestType = requestType; - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - if (client) { - eventListener.requestSendStart(streamId, requestType); - } else { - eventListener.responseSendStart(streamId, requestType, Clock.elapsedSince(receiveStartTimeNanos), - TimeUnit.NANOSECONDS); - } - } - } - - public void sendComplete() { - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - if (client) { - eventListener - .requestSendComplete(streamId, requestType, Clock.elapsedSince(startTime), Clock.unit()); - } else { - eventListener - .responseSendComplete(streamId, requestType, Clock.elapsedSince(startTime), Clock.unit()); - } - } - } - - public void sendFailed(Throwable cause) { - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - if (client) { - eventListener.requestSendFailed(streamId, requestType, Clock.elapsedSince(startTime), - Clock.unit(), cause); - } else { - eventListener.responseSendFailed(streamId, requestType, Clock.elapsedSince(startTime), Clock.unit(), - cause); - } - } - } - - public void sendCancelled() { - if (eventPublisher.isEventPublishingEnabled()) { - EventListener eventListener = eventPublisher.getEventListener(); - if (client) { - eventListener.requestSendCancelled(streamId, requestType, Clock.elapsedSince(startTime), - Clock.unit()); - } else { - eventListener.responseSendCancelled(streamId, requestType, Clock.elapsedSince(startTime), - Clock.unit()); - } - } - } - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventSource.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/EventSource.java deleted file mode 100644 index 444c794dd..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/EventSource.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -/** - * An event source that accepts an {@link EventListener}s on which events are dispatched. - * - * @param Type of the {@link EventListener} - */ -public interface EventSource { - - /** - * Registers the passed {@code listener} to this source. - * - * @param listener Listener to register. - * - * @return A subscription which can be used to cancel this listeners interest in the source. - * - * @throws IllegalStateException If the source does not accept this subscription. - */ - EventSubscription subscribe(T listener); - - /** - * A subscription of an {@link EventListener} to an {@link EventSource}. - */ - interface EventSubscription { - - /** - * Cancels the registration of the associated listener to the source. - */ - void cancel(); - - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingClientEventListener.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingClientEventListener.java deleted file mode 100644 index 24303c592..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingClientEventListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import org.slf4j.event.Level; - -import java.util.concurrent.TimeUnit; -import java.util.function.DoubleSupplier; - -public class LoggingClientEventListener extends LoggingEventListener implements ClientEventListener { - - public LoggingClientEventListener(String name, Level logLevel) { - super(name, logLevel); - } - - @Override - public void connectStart() { - logIfEnabled(() -> name + ": connectStart"); - } - - @Override - public void connectCompleted(DoubleSupplier socketAvailabilitySupplier, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": connectCompleted " + "socketAvailabilitySupplier = [" + socketAvailabilitySupplier - + "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void connectFailed(long duration, TimeUnit durationUnit, Throwable cause) { - logIfEnabled(() -> name + ": connectFailed " + "duration = [" + duration + "], durationUnit = [" + - durationUnit + "], cause = [" + cause + ']'); - } - - @Override - public void connectCancelled(long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": connectCancelled " + "duration = [" + duration + "], durationUnit = [" + - durationUnit + ']'); - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingEventListener.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingEventListener.java deleted file mode 100644 index 4fa580cce..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingEventListener.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import io.reactivesocket.FrameType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; - -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -public class LoggingEventListener implements EventListener { - - private final Logger logger; - - protected final String name; - protected final Level logLevel; - - public LoggingEventListener(String name, Level logLevel) { - this.name = name; - this.logLevel = logLevel; - logger = LoggerFactory.getLogger(name); - } - - @Override - public void requestReceiveStart(int streamId, RequestType type) { - logIfEnabled(() -> name + ": requestReceiveStart " + "streamId = [" + streamId + "], type = [" + type + ']'); - } - - @Override - public void requestReceiveComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": requestReceiveComplete " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void requestReceiveFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) { - logIfEnabled(() -> name + ": requestReceiveFailed " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + "], cause = [" - + cause + ']'); - } - - @Override - public void requestReceiveCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": requestReceiveCancelled " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void requestSendStart(int streamId, RequestType type) { - logIfEnabled(() -> name + ": requestSendStart " + "streamId = [" + streamId + "], type = [" + type + ']'); - } - - @Override - public void requestSendComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": requestSendComplete " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void requestSendFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) { - logIfEnabled(() -> name + ": requestSendFailed " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + "], cause = [" + - cause + ']'); - } - - @Override - public void requestSendCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": requestSendCancelled " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void responseSendStart(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": responseSendStart " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void responseSendComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": responseSendComplete " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void responseSendFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) { - System.out.println(name + ": responseSendFailed " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + "], cause = [" + - cause + ']'); - } - - @Override - public void responseSendCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": responseSendCancelled " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void responseReceiveStart(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": responseReceiveStart " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void responseReceiveComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": responseReceiveComplete " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void responseReceiveFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) { - logIfEnabled(() -> name + ": responseReceiveFailed " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + "], cause = [" + - cause + ']'); - } - - @Override - public void responseReceiveCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": responseReceiveCancelled " + "streamId = [" + streamId + "], type = [" + type + - "], duration = [" + duration + "], durationUnit = [" + durationUnit + ']'); - } - - @Override - public void socketClosed(long duration, TimeUnit durationUnit) { - logIfEnabled(() -> name + ": socketClosed " + "duration = [" + duration + "], durationUnit = [" + - durationUnit + ']'); - } - - @Override - public void frameWritten(int streamId, FrameType frameType) { - logIfEnabled(() -> name + ": frameWritten " + "streamId = [" + streamId + "], frameType = [" + frameType + ']'); - } - - @Override - public void frameRead(int streamId, FrameType frameType) { - logIfEnabled(() -> name + ": frameRead " + "streamId = [" + streamId + "], frameType = [" + frameType + ']'); - } - - @Override - public void leaseSent(int permits, int ttl) { - logIfEnabled(() -> name + ": leaseSent " + "permits = [" + permits + "], ttl = [" + ttl + ']'); - } - - @Override - public void leaseReceived(int permits, int ttl) { - logIfEnabled(() -> name + ": leaseReceived " + "permits = [" + permits + "], ttl = [" + ttl + ']'); - } - - @Override - public void errorSent(int streamId, int errorCode) { - logIfEnabled(() -> name + ": errorSent " + "streamId = [" + streamId + "], errorCode = [" + errorCode + ']'); - } - - @Override - public void errorReceived(int streamId, int errorCode) { - logIfEnabled(() -> name + ": errorReceived " + "streamId = [" + streamId + "], errorCode = [" + errorCode + ']'); - } - - @Override - public void dispose() { - logIfEnabled(() -> name + ": dispose"); - } - - protected void logIfEnabled(Supplier logMsgSupplier) { - switch (logLevel) { - case ERROR: - if (logger.isErrorEnabled()) { - logger.error(logMsgSupplier.get()); - } - break; - case WARN: - if (logger.isWarnEnabled()) { - logger.warn(logMsgSupplier.get()); - } - break; - case INFO: - if (logger.isInfoEnabled()) { - logger.info(logMsgSupplier.get()); - } - break; - case DEBUG: - if (logger.isDebugEnabled()) { - logger.debug(logMsgSupplier.get()); - } - break; - case TRACE: - if (logger.isTraceEnabled()) { - logger.trace(logMsgSupplier.get()); - } - break; - } - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingServerEventListener.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingServerEventListener.java deleted file mode 100644 index dcf88fe7b..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/LoggingServerEventListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import org.slf4j.event.Level; - -public class LoggingServerEventListener extends LoggingEventListener implements ServerEventListener { - - public LoggingServerEventListener(String name, Level logLevel) { - super(name, logLevel); - } - - @Override - public void socketAccepted() { - logIfEnabled(() -> name + ": socketAccepted "); - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/events/ServerEventListener.java b/reactivesocket-core/src/main/java/io/reactivesocket/events/ServerEventListener.java deleted file mode 100644 index f072721e7..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/events/ServerEventListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.events; - -import java.util.function.DoubleSupplier; -import java.util.function.Supplier; - -/** - * {@link EventListener} for a server. - */ -public interface ServerEventListener extends EventListener { - - /** - * When a new socket is accepted. - */ - default void socketAccepted() {} - -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/DisabledEventPublisher.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/DisabledEventPublisher.java deleted file mode 100644 index 79801f750..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/DisabledEventPublisher.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.internal; - -import io.reactivesocket.events.EventListener; - -public class DisabledEventPublisher extends EventPublisherImpl { -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/EventPublisher.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/EventPublisher.java deleted file mode 100644 index 2ecff29ed..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/EventPublisher.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.internal; - -import io.reactivesocket.events.EventListener; -import io.reactivesocket.events.EventSource.EventSubscription; - -public interface EventPublisher extends EventSubscription { - - /** - * @return {@link EventListener} associated with this publisher. Maybe {@code null} if event publishing disabled. - */ - T getEventListener(); - - /** - * @return {@code true} if event publishing is enabled. - */ - default boolean isEventPublishingEnabled() { - return false; - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/EventPublisherImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/EventPublisherImpl.java deleted file mode 100644 index 56daf8d99..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/EventPublisherImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.internal; - -import io.reactivesocket.events.EventListener; - -public class EventPublisherImpl implements EventPublisher { - - private final T listener; - private volatile boolean enabled; - - protected EventPublisherImpl() { - listener = null; - enabled = false; - } - - public EventPublisherImpl(T listener) { - this.listener = listener; - enabled = true; - } - - @Override - public T getEventListener() { - return listener; - } - - @Override - public boolean isEventPublishingEnabled() { - return enabled; - } - - @Override - public void cancel() { - enabled = false; - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/FlowControlHelper.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/FlowControlHelper.java deleted file mode 100644 index c470c1596..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/FlowControlHelper.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.internal; - -public final class FlowControlHelper { - - private FlowControlHelper() { - } - - /** - * Increment {@code existing} value with {@code toAdd}. - * - * @param existing Existing value of {@code requestN} - * @param toAdd Value to increment by. - * - * @return New {@code requestN} value capped at {@link Integer#MAX_VALUE}. - */ - public static int incrementRequestN(int existing, int toAdd) { - if (existing == Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } - - final int u = existing + toAdd; - return u < 0 ? Integer.MAX_VALUE : u; - } - - /** - * Increment {@code existing} value with {@code toAdd}. - * - * @param existing Existing value of {@code requestN} - * @param toAdd Value to increment by. - * - * @return New {@code requestN} value capped at {@link Integer#MAX_VALUE}. - */ - public static int incrementRequestN(int existing, long toAdd) { - if (existing == Integer.MAX_VALUE || toAdd >= Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } - - final int u = existing + (int)toAdd; // Safe downcast: Since toAdd can not be > Integer.MAX_VALUE here. - return u < 0 ? Integer.MAX_VALUE : u; - } - - /** - * Increment existing by add and if there is overflow return Long.MAX_VALUE - */ - public static long incrementRequestN(long existing, long toAdd) { - long l = existing + toAdd; - return l < 0 ? Long.MAX_VALUE : l; - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/MonoOnErrorOrCancelReturn.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/MonoOnErrorOrCancelReturn.java deleted file mode 100644 index fb50b881f..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/MonoOnErrorOrCancelReturn.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.internal; - -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import reactor.core.publisher.MonoSource; -import reactor.core.publisher.Operators; - -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.function.Function; -import java.util.function.Supplier; - -public class MonoOnErrorOrCancelReturn extends MonoSource { - final Function onError; - final Supplier onCancel; - - public MonoOnErrorOrCancelReturn(Publisher source, Function onError, Supplier onCancel) { - super(source); - this.onError = Objects.requireNonNull(onError, "onError"); - this.onCancel = Objects.requireNonNull(onCancel, "onCancel"); - } - - @Override - public void subscribe(Subscriber s) { - source.subscribe(new OnErrorOrCancelReturnSubscriber(s, onError, onCancel)); - } - - static final class OnErrorOrCancelReturnSubscriber extends Operators.MonoSubscriber { - final Function onError; - final Supplier onCancel; - - Subscription s; - - int count; - - boolean done; - - public OnErrorOrCancelReturnSubscriber(Subscriber actual, Function onError, Supplier onCancel) { - super(actual); - this.onError = onError; - this.onCancel = onCancel; - } - - @Override - public void request(long n) { - super.request(n); - if (n > 0L) { - s.request(Long.MAX_VALUE); - } - } - - @Override - public void cancel() { - s.cancel(); - complete(onCancel.get()); - } - - @Override - public void onSubscribe(Subscription s) { - if (Operators.validate(this.s, s)) { - this.s = s; - actual.onSubscribe(this); - } - } - - @Override - @SuppressWarnings("unchecked") - public void onNext(T t) { - if (done) { - Operators.onNextDropped(t); - return; - } - value = t; - - if (++count > 1) { - cancel(); - - onError(new IndexOutOfBoundsException("Source emitted more than one item")); - } - } - - @Override - public void onError(Throwable t) { - if (done) { - Operators.onErrorDropped(t); - return; - } - done = true; - - complete(onError.apply(t)); - } - - @Override - public void onComplete() { - if (done) { - return; - } - done = true; - - int c = count; - if (c == 0) { - actual.onError(Operators.onOperatorError(this, - new NoSuchElementException("Source was empty"))); - } else if (c == 1) { - complete(value); - } - } - } -} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteReceiver.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteReceiver.java deleted file mode 100644 index d35fd3d37..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteReceiver.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.internal; - -import io.reactivesocket.DuplexConnection; -import io.reactivesocket.Frame; -import io.reactivesocket.Payload; -import io.reactivesocket.exceptions.ApplicationException; -import io.reactivesocket.exceptions.CancelException; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import reactor.core.publisher.FluxProcessor; - -/** - * An abstraction to receive data from a {@link Publisher} that is available remotely over a {@code ReactiveSocket} - * connection. In order to achieve that, this class provides the following contracts: - * - *

      - *
    • A {@link Publisher} that can be subscribed to receive data from the remote peer.
    • - *
    • A {@link Subscriber} that subscribes to the original data {@code Publisher} that receives {@code ReactiveSocket} - * frames from {@link DuplexConnection}
    • - *
    - * - *

    Flow Control

    - * - * This class sends {@link Subscription} events over the wire to the remote peer. This is done via - * {@link DuplexConnection#send(Publisher)} where the stream is the stream of {@code RequestN} and {@code Cancel} - * frames.

    - * {@code RequestN} from the {@code Subscriber} to this {@code Publisher} are sent as-is to the remote peer and the - * original {@code Publisher} for reading frames.

    - * This class honors any write flow control imposed by {@link DuplexConnection#send(Publisher)} to control the - * {@code RequestN} and {@code Cancel} frames sent over the wire. This means that if the underlying connection isn't - * ready to write, no frames will be enqueued into the connection. All {@code RequestN} frames sent during such time - * will be merged into a single {@code RequestN} frame. - */ -public final class RemoteReceiver extends FluxProcessor { - - private final Publisher transportSource; - private final DuplexConnection connection; - private final int streamId; - private final Runnable cleanup; - private final Frame requestFrame; - private final Subscription transportSubscription; - private final boolean sendRequestN; - private volatile ValidatingSubscription subscription; - private volatile Subscription sourceSubscription; - private volatile boolean missedComplete; - private volatile Throwable missedError; - - public RemoteReceiver(Publisher transportSource, DuplexConnection connection, int streamId, - Runnable cleanup, boolean sendRequestN) { - this.transportSource = transportSource; - this.connection = connection; - this.streamId = streamId; - this.cleanup = cleanup; - this.sendRequestN = sendRequestN; - requestFrame = null; - transportSubscription = null; - } - - public RemoteReceiver(DuplexConnection connection, int streamId, Runnable cleanup, Frame requestFrame, - Subscription transportSubscription, boolean sendRequestN) { - this.requestFrame = requestFrame; - this.transportSubscription = transportSubscription; - transportSource = null; - this.connection = connection; - this.streamId = streamId; - this.cleanup = cleanup; - this.sendRequestN = sendRequestN; - } - - @Override - public void subscribe(Subscriber s) { - final SubscriptionFramesSource framesSource = new SubscriptionFramesSource(); - boolean _missed; - synchronized (this) { - if (subscription != null && subscription.isActive()) { - throw new IllegalStateException("Duplicate subscriptions not allowed."); - } - _missed = missedComplete || null != missedError; - if (!_missed) { - // Since, the subscriber to this subscription is not started (via onSubscribe) till we receive - // onSubscribe on this class, the callbacks here will always find sourceSubscription. - subscription = ValidatingSubscription.create(s, () -> { - sourceSubscription.cancel(); - framesSource.sendCancel(); - cleanup.run(); - }, requestN -> { - sourceSubscription.request(requestN); - if (sendRequestN) { - framesSource.sendRequestN(requestN); - } - }); - } - } - - if (_missed) { - s.onSubscribe(ValidatingSubscription.empty()); - if (null != missedError) { - s.onError(missedError); - } else { - s.onComplete(); - } - return; - } - - if (transportSource != null) { - transportSource.subscribe(this); - } else if (transportSubscription != null) { - onSubscribe(transportSubscription); - onNext(requestFrame); - } - connection.send(framesSource) - .doOnError(throwable -> subscription.safeOnError(throwable)) - .subscribe(); - } - - @Override - public void onSubscribe(Subscription s) { - boolean cancelThis; - synchronized (this) { - cancelThis = sourceSubscription != null/*ReactiveStreams rule 2.5*/ || !subscription.isActive(); - if (!cancelThis) { - sourceSubscription = s; - } - } - - if (cancelThis) { - s.cancel(); - } else { - // Do not start the subscriber to the Publisher till this Subscriber is started. This avoids race conditions - // and hence caching of requestN and cancel from downstream without upstream being ready. - subscription.getSubscriber().onSubscribe(subscription); - } - } - - @Override - public void onNext(Frame frame) { - synchronized (this) { - if (subscription == null) { - throw new IllegalStateException("Received onNext before subscription."); - } - } - switch (frame.getType()) { - case ERROR: - onError(new ApplicationException(frame)); - break; - case NEXT: - subscription.safeOnNext(frame); - break; - case COMPLETE: - onComplete(); - break; - case NEXT_COMPLETE: - subscription.safeOnNext(frame); - onComplete(); - break; - } - } - - @Override - public void onError(Throwable t) { - boolean _missed = false; - synchronized (this) { - if (subscription == null) { - _missed = true; - missedError = t; - } - } - if (!_missed) { - subscription.safeOnError(t); - } - cleanup.run(); - } - - @Override - public void onComplete() { - boolean _missed = false; - synchronized (this) { - if (subscription == null) { - _missed = true; - missedComplete = true; - } - } - if (!_missed) { - subscription.safeOnComplete(); - } - cleanup.run(); - } - - public void cancel() { - sourceSubscription.cancel(); - // Since, source subscription is cancelled, send an error to the subscriber to cleanup. - onError(new CancelException("Remote subscription cancelled.")); - } - - private class SubscriptionFramesSource implements Publisher { - - private ValidatingSubscription subscription; - private int requested; // Guarded by this. - private int bufferedRequestN; // Guarded by this. - private boolean bufferedCancel; // Guarded by this. - - @Override - public void subscribe(Subscriber s) { - subscription = ValidatingSubscription.onRequestN(s, requestN -> { - boolean sendCancel; - int n; - synchronized (this) { - requested = FlowControlHelper.incrementRequestN(requested, requestN); - sendCancel = bufferedCancel; - n = bufferedRequestN; - } - if (sendCancel) { - subscription.safeOnNext(Frame.Cancel.from(streamId)); - } else if (sendRequestN && n > 0) { - subscription.safeOnNext(Frame.RequestN.from(streamId, n)); - } - }); - s.onSubscribe(subscription); - } - - public void sendRequestN(final long n) { - final int toRequest; - final ValidatingSubscription sub; - synchronized (this) { - sub = subscription; - if (requested > 0) { - toRequest = FlowControlHelper.incrementRequestN(bufferedRequestN, n); - bufferedRequestN = 0; // Reset the buffer since this requestN will be sent. - requested--; - } else { - bufferedRequestN = FlowControlHelper.incrementRequestN(bufferedRequestN, n); - toRequest = 0; - } - } - if (sub != null && sub.isActive() && toRequest > 0) { - sub.safeOnNext(Frame.RequestN.from(streamId, toRequest)); - } - } - - public void sendCancel() { - final boolean send; - synchronized (this) { - send = requested > 0; - if (send) { - requested--; - } else { - bufferedCancel = true; - } - } - if (send) { - subscription.safeOnNext(Frame.Cancel.from(streamId)); - } - } - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java deleted file mode 100644 index f50b6866d..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/RemoteSender.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.internal; - -import io.reactivesocket.DuplexConnection; -import io.reactivesocket.Frame; -import io.reactivesocket.Frame.RequestN; -import io.reactivesocket.FrameType; -import org.reactivestreams.Processor; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -/** - * An abstraction to convert a {@link Publisher} to send it over a {@code ReactiveSocket} connection. In order to - * achieve that, this class provides the following contracts: - * - *

      - *
    • A {@link Publisher} that can be written to a {@code DuplexConnection} using - {@link DuplexConnection#send(Publisher)}
    • - *
    • A {@link Subscriber} that subscribes to the original data {@code Publisher}
    • - *
    • A {@link Subscription} that can be used to accept cancellations and flow control, typically from the peer of the - * {@code ReactiveSocket}.
    • - *
    - * - *

    Subscriptions

    - * - * Logically there are two subscriptions to this {@code Processor}. One from {@link DuplexConnection#send(Publisher)} - * and other logical subscription from the peer of the {@code ReactiveSocket} this stream is sent to. - * The {@link Subscription} contract on this class is to receive {@code Subscription} signals from the remote peer i.e. - * typically via a {@code RequestN} and {@code Cancel} frames. However, cancellations may be used to signal a cancel of - * the write, typically due to clean shutdown of the associated {@code ReactiveSocket}. - * - *

    Flow Control

    - * - * This class mediates between the flow control between the transport and {@code ReactiveSocket} peer, so - * to make sure that a higher demand from transport does not overwrite lower demand from the peer and vice-versa. - * This feature is different than a regular {@link Processor}. - * - *

    Flow control with terminal events

    - * - * Since, reactive-streams terminal events ({@code onComplete} and {@code onError}) are not flow controlled and - * {@code ReactiveSocket} requires the terminal frames to be sent over the wire, this may bring a situation where - * transport is not available to write and hence these terminal signals have to be buffered. This is the only place - * where frames are buffered, otherwise, {@link #onNext(Frame)} here would not buffer or check for flow control. - */ -public final class RemoteSender implements Processor, Subscription { - - private final Publisher originalSource; - private final Runnable cleanup; - private final int streamId; - private volatile ValidatingSubscription transportSubscription; - private volatile Subscription sourceSubscription; - - private int transportRequested; // Guarded by this - private int remoteRequested; // Guarded by this - private int outstanding; // Guarded by this - private Frame bufferedTerminalFrame; // Guarded by this - private Throwable bufferedTransportError; // Guarded by this - - public RemoteSender(Publisher originalSource, Runnable cleanup, int streamId, int initialRemoteRequested) { - this.originalSource = originalSource; - this.cleanup = cleanup; - this.streamId = streamId; - remoteRequested = initialRemoteRequested; - } - - public RemoteSender(Publisher originalSource, Runnable cleanup, int streamId) { - this(originalSource, cleanup, streamId, 0); - } - - @Override - public void subscribe(Subscriber s) { - // Subscription from DuplexConnection (on send) - ValidatingSubscription sub; - synchronized (this) { - if (transportSubscription != null && transportSubscription.isActive()) { - throw new IllegalStateException("Duplicate subscriptions not allowed."); - } - transportSubscription = ValidatingSubscription.create(s, () -> { - final Subscription sourceSub; - synchronized (this) { - if (sourceSubscription == null) { - return; - } - sourceSub = sourceSubscription; - } - sourceSub.cancel(); - cleanup.run(); - }, requestN -> { - final Frame bufferedTerminalFrame; - synchronized (this) { - bufferedTerminalFrame = this.bufferedTerminalFrame; - transportRequested = FlowControlHelper.incrementRequestN(transportRequested, requestN); - } - if (bufferedTerminalFrame != null) { - unsafeSendTerminalFrameToTransport(bufferedTerminalFrame, bufferedTransportError); - cleanup.run(); - } else { - tryRequestN(); - } - }); - sub = transportSubscription; - } - // Starting transport subscription (via onSubscribe) before subscribing to original, so no buffering required. - s.onSubscribe(sub); - originalSource.subscribe(this); - } - - @Override - public void onSubscribe(Subscription s) { - boolean cancelThis; - synchronized (this) { - cancelThis = sourceSubscription != null/*ReactiveStreams rule 2.5*/ || !transportSubscription.isActive(); - if (!cancelThis) { - sourceSubscription = s; - } - } - if (cancelThis) { - s.cancel(); - } else { - tryRequestN(); - } - } - - @Override - public void onNext(Frame frame) { - // No flow-control check - FrameType frameType = frame.getType(); - assert frameType != FrameType.ERROR && !isCompleteFrame(frameType); - synchronized (this) { - outstanding--; - } - transportSubscription.safeOnNext(frame); - } - - @Override - public void onError(Throwable t) { - if (trySendTerminalFrame(Frame.Error.from(streamId, t), t)) { - transportSubscription.safeOnError(t); - cleanup.run(); - } - } - - @Override - public void onComplete() { - if (trySendTerminalFrame(Frame.PayloadFrame.from(streamId, FrameType.COMPLETE), null)) { - transportSubscription.safeOnComplete(); - cleanup.run(); - } - } - - public void acceptRequestNFrame(Frame requestNFrame) { - request(RequestN.requestN(requestNFrame)); - } - - public void acceptCancelFrame(Frame cancelFrame) { - assert cancelFrame.getType() == FrameType.CANCEL; - cancel(); - } - - @Override - public synchronized void request(long requestN) { - synchronized (this) { - remoteRequested = FlowControlHelper.incrementRequestN(remoteRequested, requestN); - } - tryRequestN(); - } - - @Override - public void cancel() { - sourceSubscription.cancel(); - transportSubscription.cancel(); - cleanup.run(); - } - - private void tryRequestN() { - int _toRequest; - synchronized (this) { - if (sourceSubscription == null) { - return; - } - // Request upto remoteRequested but never more than transportRequested. - _toRequest = Math.min(transportRequested, remoteRequested); - outstanding = FlowControlHelper.incrementRequestN(outstanding, _toRequest); - if (outstanding < transportRequested) { - // Terminal frames are not accounted in remoteRequested, so increment by 1 if transport can accomodate. - ++outstanding; - } - transportRequested -= _toRequest; - remoteRequested -= _toRequest; - } - - if (_toRequest > 0) { - sourceSubscription.request(_toRequest); - } - } - - private boolean trySendTerminalFrame(Frame frame, Throwable optionalError) { - boolean send; - synchronized (this) { - send = outstanding > 0; - if (!send && bufferedTerminalFrame == null) { - bufferedTerminalFrame = frame; - bufferedTransportError = optionalError; - } - } - - if (send) { - unsafeSendTerminalFrameToTransport(frame, optionalError); - } - return send; - } - - private void unsafeSendTerminalFrameToTransport(Frame terminalFrame, Throwable optionalError) { - transportSubscription.safeOnNext(terminalFrame); - if (terminalFrame.getType() == FrameType.COMPLETE || terminalFrame.getType() == FrameType.NEXT_COMPLETE) { - transportSubscription.safeOnComplete(); - } else { - transportSubscription.safeOnError(optionalError); - } - } - - private static boolean isCompleteFrame(FrameType frameType) { - return frameType == FrameType.COMPLETE || frameType == FrameType.NEXT_COMPLETE; - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java b/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java index 2a7d33977..4f7dfdfcb 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java @@ -15,24 +15,19 @@ import io.reactivesocket.ClientReactiveSocket; import io.reactivesocket.ConnectionSetupPayload; -import io.reactivesocket.DuplexConnection; import io.reactivesocket.FrameType; import io.reactivesocket.ServerReactiveSocket; import io.reactivesocket.StreamIdSupplier; import io.reactivesocket.client.KeepAliveProvider; -import io.reactivesocket.events.AbstractEventSource; -import io.reactivesocket.events.ConnectionEventInterceptor; -import io.reactivesocket.events.ServerEventListener; import io.reactivesocket.internal.ClientServerInputMultiplexer; import io.reactivesocket.lease.DefaultLeaseHonoringSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.lease.LeaseHonoringSocket; import io.reactivesocket.transport.TransportServer; import io.reactivesocket.transport.TransportServer.StartedServer; -import io.reactivesocket.util.Clock; import reactor.core.publisher.Mono; -public final class DefaultReactiveSocketServer extends AbstractEventSource +public final class DefaultReactiveSocketServer implements ReactiveSocketServer { private final TransportServer transportServer; @@ -44,22 +39,7 @@ public DefaultReactiveSocketServer(TransportServer transportServer) { @Override public StartedServer start(SocketAcceptor acceptor) { return transportServer.start(connection -> { - DuplexConnection dc; - if (isEventPublishingEnabled()) { - long startTime = Clock.now(); - dc = new ConnectionEventInterceptor(connection, this); - getEventListener().socketAccepted(); - dc.onClose().doFinally(signalType -> { - if (isEventPublishingEnabled()) { - getEventListener().socketClosed(Clock.elapsedSince(startTime), Clock.unit()); - } - }).subscribe(); - } else { - dc = connection; - } - - ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(dc); - + ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(connection); return multiplexer .asStreamZeroConnection() .receive() @@ -70,21 +50,19 @@ public StartedServer start(SocketAcceptor acceptor) { ClientReactiveSocket sender = new ClientReactiveSocket(multiplexer.asServerConnection(), Throwable::printStackTrace, StreamIdSupplier.serverSupplier(), - KeepAliveProvider.never(), - this); + KeepAliveProvider.never()); LeaseHonoringSocket lhs = new DefaultLeaseHonoringSocket(sender); sender.start(lhs); LeaseEnforcingSocket handler = acceptor.accept(setup, sender); ServerReactiveSocket receiver = new ServerReactiveSocket(multiplexer.asClientConnection(), handler, setup.willClientHonorLease(), - Throwable::printStackTrace, - this); + Throwable::printStackTrace); receiver.start(); - return dc.onClose(); + return connection.onClose(); } else { return Mono.error(new IllegalStateException("Invalid first frame on the connection: " - + dc + ", frame type received: " + + connection + ", frame type received: " + setupFrame.getType())); } }); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/server/ReactiveSocketServer.java b/reactivesocket-core/src/main/java/io/reactivesocket/server/ReactiveSocketServer.java index fb5f23d27..2cb93195c 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/server/ReactiveSocketServer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/server/ReactiveSocketServer.java @@ -18,14 +18,12 @@ import io.reactivesocket.ConnectionSetupPayload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.events.EventSource; -import io.reactivesocket.events.ServerEventListener; import io.reactivesocket.exceptions.SetupException; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.transport.TransportServer; import io.reactivesocket.transport.TransportServer.StartedServer; -public interface ReactiveSocketServer extends EventSource { +public interface ReactiveSocketServer { /** * Starts this server. diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/ReactiveSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/ReactiveSocketTest.java index 08f50ae96..a758d6739 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/ReactiveSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/ReactiveSocketTest.java @@ -21,6 +21,7 @@ import io.reactivesocket.test.util.LocalDuplexConnection; import io.reactivesocket.util.PayloadImpl; import org.hamcrest.MatcherAssert; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; @@ -51,6 +52,7 @@ public void testRequestReplyNoError() { } @Test(timeout = 2000) + @Ignore public void testHandlerEmitsError() { rule.setRequestAcceptor(new AbstractReactiveSocket() { @Override diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java deleted file mode 100644 index 7cd5bd8cf..000000000 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteReceiverTest.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.internal; - -import io.reactivesocket.Frame; -import io.reactivesocket.FrameType; -import io.reactivesocket.Payload; -import io.reactivesocket.exceptions.ApplicationException; -import io.reactivesocket.exceptions.CancelException; -import io.reactivesocket.test.util.TestDuplexConnection; -import io.reactivesocket.util.PayloadImpl; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import io.reactivex.subscribers.TestSubscriber; -import reactor.core.publisher.UnicastProcessor; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; - -public class RemoteReceiverTest { - - @Rule - public final ReceiverRule rule = new ReceiverRule(); - - @Test - public void testCompleteFrame() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToReceiver(); - rule.assertRequestNSent(1); - rule.sendFrame(FrameType.COMPLETE); - - receiverSub.assertComplete(); - assertThat("Receiver not cleaned up.", rule.receiverCleanedUp, is(true)); - } - - @Test - public void testErrorFrame() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToReceiver(); - rule.assertRequestNSent(1); - rule.sendFrame(Frame.Error.from(rule.streamId, new ApplicationException(PayloadImpl.EMPTY))); - - receiverSub.assertNotComplete(); - receiverSub.assertError(ApplicationException.class); - assertThat("Receiver not cleaned up.", rule.receiverCleanedUp, is(true)); - } - - @Test - public void testNextFrame() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToReceiver(); - rule.assertRequestNSent(1); - rule.sendFrame(FrameType.NEXT); - - receiverSub.assertValueCount(1); - receiverSub.assertNotTerminated(); - assertThat("Receiver cleaned up.", rule.receiverCleanedUp, is(false)); - } - - @Test - public void testNextCompleteFrame() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToReceiver(); - rule.assertRequestNSent(1); - rule.sendFrame(FrameType.NEXT_COMPLETE); - - receiverSub.assertValueCount(1); - receiverSub.assertComplete().assertNoErrors(); - assertThat("Receiver not cleaned up.", rule.receiverCleanedUp, is(true)); - } - - @Test - public void testCancel() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToReceiver(); - rule.assertRequestNSent(1); - rule.connection.clearSendReceiveBuffers(); - rule.receiver.cancel(); - - receiverSub.assertNoValues().assertError(CancelException.class); - assertThat("Receiver not cleaned up.", rule.receiverCleanedUp, is(true)); - - rule.source.onNext(rule.newFrame(FrameType.NEXT)); - receiverSub.assertNoValues(); - } - - @Test - public void testRequestNBufferBeforeWriteReady() throws Exception { - rule.connection.setInitialSendRequestN(0); - final TestSubscriber receiverSub = rule.subscribeToReceiver(0); - assertThat("Unexpected send subscribers on the connection.", rule.connection.getSendSubscribers(), hasSize(1)); - TestSubscriber sendSubscriber = rule.connection.getSendSubscribers().iterator().next(); - assertThat("Unexpected frames sent on the connection.", rule.connection.getSent(), is(empty())); - receiverSub.request(7); - receiverSub.request(8); - - sendSubscriber.request(1);// Now request to send requestN frame. - rule.assertRequestNSent(15); // Cumulate requestN post buffering - } - - @Test - public void testCancelBufferBeforeWriteReady() throws Exception { - rule.connection.setInitialSendRequestN(0); - final TestSubscriber receiverSub = rule.subscribeToReceiver(0); - assertThat("Unexpected send subscribers on the connection.", rule.connection.getSendSubscribers(), hasSize(1)); - TestSubscriber sendSubscriber = rule.connection.getSendSubscribers().iterator().next(); - assertThat("Unexpected frames sent on the connection.", rule.connection.getSent(), is(empty())); - receiverSub.cancel(); - - sendSubscriber.request(1);// Now request to send cancel frame. - rule.assertCancelSent(); - assertThat("Receiver not cleaned up.", rule.receiverCleanedUp, is(true)); - } - - @Test - public void testMissedComplete() throws Exception { - rule.receiver.onComplete(); - final TestSubscriber receiverSub = TestSubscriber.create(); - rule.receiver.subscribe(receiverSub); - receiverSub.assertComplete().assertNoErrors(); - } - - @Test - public void testMissedError() throws Exception { - rule.receiver.onError(new NullPointerException("Deliberate exception")); - final TestSubscriber receiverSub = TestSubscriber.create(); - rule.receiver.subscribe(receiverSub); - receiverSub.assertError(NullPointerException.class).assertNotComplete(); - } - - @Test(expected = IllegalStateException.class) - public void testOnNextWithoutSubscribe() throws Exception { - rule.receiver.onNext(Frame.RequestN.from(1, 1)); - } - - public static class ReceiverRule extends ExternalResource { - - private TestDuplexConnection connection; - private UnicastProcessor source; - private RemoteReceiver receiver; - private boolean receiverCleanedUp; - private int streamId; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - connection = new TestDuplexConnection(); - streamId = 10; - source = UnicastProcessor.create(); - receiver = new RemoteReceiver(connection, streamId, () -> receiverCleanedUp = true, null, null, - true); - base.evaluate(); - } - }; - } - - public Frame newFrame(FrameType frameType) { - return Frame.PayloadFrame.from(streamId, frameType); - } - - public TestSubscriber subscribeToReceiver(int initialRequestN) { - final TestSubscriber receiverSub = TestSubscriber.create(initialRequestN); - receiver.subscribe(receiverSub); - source.subscribe(receiver); - - receiverSub.assertNotTerminated(); - return receiverSub; - } - - public TestSubscriber subscribeToReceiver() { - return subscribeToReceiver(1); - } - - public void sendFrame(FrameType frameType) { - source.onNext(newFrame(frameType)); - } - - public void sendFrame(Frame frame) { - source.onNext(frame); - } - - public void assertRequestNSent(int requestN) { - assertThat("Unexpected frames sent.", connection.getSent(), hasSize(greaterThanOrEqualTo(1))); - Frame next = connection.getSent().iterator().next(); - assertThat("Unexpected frame type.", next.getType(), is(FrameType.REQUEST_N)); - assertThat("Unexpected requestN sent.", Frame.RequestN.requestN(next), is(requestN)); - } - - public void assertCancelSent() { - assertThat("Unexpected frames sent.", connection.getSent(), hasSize(greaterThanOrEqualTo(1))); - Frame next = connection.getSent().iterator().next(); - assertThat("Unexpected frame type.", next.getType(), is(FrameType.CANCEL)); - } - } -} \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java deleted file mode 100644 index 264043e90..000000000 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/RemoteSenderTest.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.reactivesocket.internal; - -import io.reactivesocket.Frame; -import io.reactivesocket.FrameType; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import io.reactivex.subscribers.TestSubscriber; -import reactor.core.publisher.UnicastProcessor; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -public class RemoteSenderTest { - - @Rule - public final SenderRule rule = new SenderRule(); - - @Test - public void testOnNext() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(); - rule.sender.acceptRequestNFrame(Frame.RequestN.from(rule.streamId, 1)); - rule.sendFrame(FrameType.NEXT); - - receiverSub.assertValueCount(1); - receiverSub.assertValue(frame -> frame.getType() == FrameType.NEXT); - assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(false)); - } - - @Test - public void testOnError() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(); - rule.sender.onError(new NullPointerException("deliberate test exception.")); - - receiverSub.assertValueCount(1); - receiverSub.assertValue(frame -> frame.getType() == FrameType.ERROR); - receiverSub.assertError(NullPointerException.class); - receiverSub.assertNotComplete(); - assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(true)); - } - - @Test - public void testOnComplete() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(); - rule.sender.onComplete(); - - receiverSub.assertValueCount(1); - receiverSub.assertValue(frame -> frame.getType() == FrameType.COMPLETE || frame.getType() == FrameType.NEXT_COMPLETE); - - receiverSub.assertNoErrors(); - receiverSub.assertComplete(); - assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(true)); - } - - @Test - public void testTransportCancel() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(2); - rule.sender.acceptRequestNFrame(Frame.RequestN.from(rule.streamId, 2)); - rule.sendFrame(FrameType.NEXT); - - receiverSub.assertValueCount(1); - receiverSub.assertValue(frame -> frame.getType() == FrameType.NEXT); - receiverSub.cancel();// Transport cancel. - assertThat("Sender not cleaned up.", rule.senderCleanedUp, is(true)); - - rule.sendFrame(FrameType.NEXT); - receiverSub.assertValueCount(1); - } - - @Test - public void testRemoteCancel() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(2); - rule.sender.acceptRequestNFrame(Frame.RequestN.from(rule.streamId, 2)); - rule.sendFrame(FrameType.NEXT); - - receiverSub.assertValueCount(1); - receiverSub.assertValue(frame -> frame.getType() == FrameType.NEXT); - rule.sender.acceptCancelFrame(Frame.Cancel.from(rule.streamId));// Remote cancel. - assertThat("Sender not cleaned up.", rule.senderCleanedUp, is(true)); - - rule.sendFrame(FrameType.NEXT); - receiverSub.assertValueCount(1); - } - - @Test - public void testOnCompleteWithBuffer() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(0); - rule.sender.onComplete(); // buffer this terminal event. - - receiverSub.assertNotTerminated(); - receiverSub.request(1); // Now get completion - - receiverSub.assertValueCount(1); - receiverSub.assertValue(frame -> frame.getType() == FrameType.COMPLETE || frame.getType() == FrameType.NEXT_COMPLETE); - receiverSub.assertNoErrors(); - receiverSub.assertComplete(); - assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(true)); - } - - @Test - public void testOnErrorWithBuffer() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(0); - rule.sender.onError(new NullPointerException("deliberate test exception.")); // buffer this terminal event. - - receiverSub.assertNotTerminated(); - receiverSub.request(1); // Now get completion - - receiverSub.assertValueCount(1); - receiverSub.assertValue(frame -> frame.getType() == FrameType.ERROR); - receiverSub.assertError(NullPointerException.class); - receiverSub.assertNotComplete(); - assertThat("Unexpected sender cleaned up.", rule.senderCleanedUp, is(true)); - } - - @Test - public void testTransportRequestedMore() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(2); - rule.sender.request(1); // Remote requested 1 - rule.sendFrame(FrameType.NEXT); - rule.sendFrame(FrameType.NEXT); // Second onNext gets buffered. - - receiverSub.assertValueCount(1).assertNotTerminated(); - rule.sender.request(1); // Remote requested 1 to now emit second. - receiverSub.assertValueCount(2).assertNotTerminated(); - - receiverSub.request(1); // Transport: 1, remote: 0 - rule.sendFrame(FrameType.NEXT); - receiverSub.assertValueCount(2).assertNotTerminated(); - - rule.sender.request(1); // Remote: 1 - receiverSub.assertValueCount(3).assertNotTerminated(); - - receiverSub.request(1); // Transport: 1 to get terminal event. - rule.source.onComplete(); - receiverSub.assertComplete().assertNoErrors(); - } - - @Test - public void testRemoteRequestedMore() throws Exception { - final TestSubscriber receiverSub = rule.subscribeToSender(0); - rule.sender.request(1); // Remote: 1, transport: 0 - rule.sendFrame(FrameType.NEXT); // buffer, transport not ready. - - receiverSub.assertNoValues().assertNotTerminated(); - receiverSub.request(1); // Remote: 1, transport: 1, emit - - receiverSub.assertValueCount(1).assertNotTerminated(); - } - - public static class SenderRule extends ExternalResource { - - private UnicastProcessor source; - private RemoteSender sender; - private boolean senderCleanedUp; - private int streamId; - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - source = UnicastProcessor.create(); - streamId = 10; - sender = new RemoteSender(source, () -> senderCleanedUp = true, streamId); - base.evaluate(); - } - }; - } - - public Frame newFrame(FrameType frameType) { - return Frame.PayloadFrame.from(streamId, frameType); - } - - public TestSubscriber subscribeToSender(int initialRequestN) { - final TestSubscriber senderSub = TestSubscriber.create(initialRequestN); - sender.subscribe(senderSub); - - senderSub.assertNotTerminated(); - return senderSub; - } - - public TestSubscriber subscribeToSender() { - return subscribeToSender(1); - } - - public void sendFrame(FrameType frameType) { - source.onNext(newFrame(frameType)); - } - - public void sendFrame(Frame frame) { - source.onNext(frame); - } - } -} \ No newline at end of file diff --git a/reactivesocket-discovery-eureka/src/main/java/io/reactivesocket/discovery/eureka/Eureka.java b/reactivesocket-discovery-eureka/src/main/java/io/reactivesocket/discovery/eureka/Eureka.java index 444e3c217..6e4ea5aee 100644 --- a/reactivesocket-discovery-eureka/src/main/java/io/reactivesocket/discovery/eureka/Eureka.java +++ b/reactivesocket-discovery-eureka/src/main/java/io/reactivesocket/discovery/eureka/Eureka.java @@ -20,9 +20,9 @@ import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.discovery.CacheRefreshedEvent; import com.netflix.discovery.EurekaClient; -import io.reactivesocket.internal.ValidatingSubscription; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; +import reactor.core.publisher.Operators; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -42,7 +42,7 @@ public Publisher> subscribeToAsg(String vip, boolean s @Override public void subscribe(Subscriber> subscriber) { // TODO: backpressure - subscriber.onSubscribe(ValidatingSubscription.empty(subscriber)); + subscriber.onSubscribe(Operators.emptySubscription()); pushChanges(subscriber); client.registerEventListener(event -> { diff --git a/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest.java b/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest.java index b600b6876..1b2dface3 100644 --- a/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest.java +++ b/reactivesocket-examples/src/test/java/io/reactivesocket/integration/IntegrationTest.java @@ -28,11 +28,13 @@ import io.reactivesocket.transport.netty.client.TcpTransportClient; import io.reactivesocket.transport.netty.server.TcpTransportServer; import io.reactivesocket.util.PayloadImpl; +import io.reactivex.subscribers.TestSubscriber; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.ipc.netty.tcp.TcpClient; import reactor.ipc.netty.tcp.TcpServer; @@ -43,8 +45,8 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; public class IntegrationTest { @@ -57,7 +59,20 @@ public void testRequest() { assertThat("Server did not see the request.", rule.requestCount.get(), is(1)); } - @Test//(timeout = 2_000L) + @Test + public void testStream() throws Exception { + TestSubscriber subscriber = TestSubscriber.create(); + rule + .client + .requestStream(new PayloadImpl("start")) + .subscribe(subscriber); + + subscriber.cancel(); + subscriber.isCancelled(); + subscriber.assertNotComplete(); + } + + @Test(timeout = 3_000L) public void testClose() throws ExecutionException, InterruptedException, TimeoutException { rule.client.close().block(); @@ -79,25 +94,32 @@ public void evaluate() throws Throwable { requestCount = new AtomicInteger(); disconnectionCounter = new CountDownLatch(1); server = ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) - .start((setup, sendingSocket) -> { - sendingSocket.onClose() - .doFinally(signalType -> disconnectionCounter.countDown()) - .subscribe(); - - return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { - @Override - public Mono requestResponse(Payload payload) { - return Mono.just(new PayloadImpl("RESPONSE", "METADATA")) - .doOnSubscribe(s -> requestCount.incrementAndGet()); - } - }); - }); + .start((setup, sendingSocket) -> { + sendingSocket.onClose() + .doFinally(signalType -> disconnectionCounter.countDown()) + .subscribe(); + + return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { + @Override + public Mono requestResponse(Payload payload) { + return Mono.just(new PayloadImpl("RESPONSE", "METADATA")) + .doOnSubscribe(s -> requestCount.incrementAndGet()); + } + + @Override + public Flux requestStream(Payload payload) { + return Flux + .range(1, 1_000_000) + .map(i -> new PayloadImpl("data -> " + i)); + } + }); + }); client = ReactiveSocketClient.create(TcpTransportClient.create(TcpClient.create(options -> - options.connect((InetSocketAddress)server.getServerAddress()))), - SetupProvider.keepAlive(KeepAliveProvider.never()) - .disableLease()) - .connect() - .block(); + options.connect((InetSocketAddress) server.getServerAddress()))), + SetupProvider.keepAlive(KeepAliveProvider.never()) + .disableLease()) + .connect() + .block(); base.evaluate(); } }; diff --git a/reactivesocket-examples/src/test/resources/log4j.properties b/reactivesocket-examples/src/test/resources/log4j.properties new file mode 100644 index 000000000..31161e8ac --- /dev/null +++ b/reactivesocket-examples/src/test/resources/log4j.properties @@ -0,0 +1,18 @@ +# +# Copyright 2016 Netflix, Inc. +#

    +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +#

    +# http://www.apache.org/licenses/LICENSE-2.0 +#

    +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +log4j.rootLogger=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %5p [%t] (%F) - %m%n +log4j.logger.io.reactivesocket.FrameLogger=Debug \ No newline at end of file diff --git a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/ClientEventListenerImpl.java b/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/ClientEventListenerImpl.java deleted file mode 100644 index ceffa4e89..000000000 --- a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/ClientEventListenerImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.spectator; - -import com.netflix.spectator.api.Counter; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Spectator; -import com.netflix.spectator.api.histogram.PercentileTimer; -import io.reactivesocket.events.ClientEventListener; - -import java.util.concurrent.TimeUnit; -import java.util.function.DoubleSupplier; - -import static io.reactivesocket.spectator.internal.SpectatorUtil.*; - -public class ClientEventListenerImpl extends EventListenerImpl implements ClientEventListener { - - private final Counter connectStarts; - private final Counter connectFailed; - private final Counter connectCancelled; - private final Counter connectSuccess; - private final PercentileTimer connectSuccessLatency; - private final PercentileTimer connectFailureLatency; - private final PercentileTimer connectCancelledLatency; - - public ClientEventListenerImpl(Registry registry, String monitorId) { - super(registry, monitorId); - connectStarts = registry.counter(createId(registry, "connectStart", monitorId)); - connectFailed = registry.counter(createId(registry, "connectFailed", monitorId)); - connectCancelled = registry.counter(createId(registry, "connectCancelled", monitorId)); - connectSuccess = registry.counter(createId(registry, "connectSuccess", monitorId)); - connectSuccessLatency = PercentileTimer.get(registry, createId(registry, "connectLatency", monitorId, - "outcome", "success")); - connectFailureLatency = PercentileTimer.get(registry, createId(registry, "connectLatency", monitorId, - "outcome", "success")); - connectCancelledLatency = PercentileTimer.get(registry, createId(registry, "connectLatency", monitorId, - "outcome", "success")); - } - - public ClientEventListenerImpl(String monitorId) { - this(Spectator.globalRegistry(), monitorId); - } - - @Override - public void connectStart() { - connectStarts.increment(); - } - - @Override - public void connectCompleted(DoubleSupplier socketAvailabilitySupplier, long duration, TimeUnit durationUnit) { - connectSuccess.increment(); - connectSuccessLatency.record(duration, durationUnit); - } - - @Override - public void connectFailed(long duration, TimeUnit durationUnit, Throwable cause) { - connectFailed.increment(); - connectFailureLatency.record(duration, durationUnit); - } - - @Override - public void connectCancelled(long duration, TimeUnit durationUnit) { - connectCancelled.increment(); - connectCancelledLatency.record(duration, durationUnit); - } -} diff --git a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/EventListenerImpl.java b/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/EventListenerImpl.java deleted file mode 100644 index 398343741..000000000 --- a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/EventListenerImpl.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.spectator; - -import com.netflix.spectator.api.Counter; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Spectator; -import io.reactivesocket.FrameType; -import io.reactivesocket.events.EventListener; -import io.reactivesocket.spectator.internal.ErrorStats; -import io.reactivesocket.spectator.internal.LeaseStats; -import io.reactivesocket.spectator.internal.RequestStats; - -import java.util.EnumMap; -import java.util.concurrent.TimeUnit; - -import static io.reactivesocket.spectator.internal.SpectatorUtil.*; - -public class EventListenerImpl implements EventListener { - - private final LeaseStats leaseStats; - private final ErrorStats errorStats; - private final Counter socketClosed; - private final Counter frameRead; - private final Counter frameWritten; - private final EnumMap requestStats; - - public EventListenerImpl(Registry registry, String monitorId) { - leaseStats = new LeaseStats(registry, monitorId); - errorStats = new ErrorStats(registry, monitorId); - socketClosed = registry.counter(createId(registry, "socketClosed", monitorId)); - frameRead = registry.counter(createId(registry, "frameRead", monitorId)); - frameWritten = registry.counter(createId(registry, "frameWritten", monitorId)); - requestStats = new EnumMap(RequestType.class); - for (RequestType type : RequestType.values()) { - requestStats.put(type, new RequestStats(registry, type, monitorId)); - } - } - - public EventListenerImpl(String monitorId) { - this(Spectator.globalRegistry(), monitorId); - } - - @Override - public void requestReceiveStart(int streamId, RequestType type) { - requestStats.get(type).requestReceivedStart(); - } - - @Override - public void requestReceiveComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).requestReceivedSuccess(duration, durationUnit); - } - - @Override - public void requestReceiveFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) { - requestStats.get(type).requestReceivedFailed(duration, durationUnit); - } - - @Override - public void requestReceiveCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).requestReceivedCancelled(duration, durationUnit); - - } - - @Override - public void requestSendStart(int streamId, RequestType type) { - requestStats.get(type).requestSendStart(); - } - - @Override - public void requestSendComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).requestSendSuccess(duration, durationUnit); - } - - @Override - public void requestSendFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) { - requestStats.get(type).requestSendFailed(duration, durationUnit); - } - - @Override - public void requestSendCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).requestSendCancelled(duration, durationUnit); - } - - @Override - public void responseSendStart(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).responseSendStart(duration, durationUnit); - } - - @Override - public void responseSendComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).responseSendSuccess(duration, durationUnit); - } - - @Override - public void responseSendFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) { - requestStats.get(type).responseSendFailed(duration, durationUnit); - } - - @Override - public void responseSendCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).responseSendCancelled(duration, durationUnit); - } - - @Override - public void responseReceiveStart(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).responseReceivedStart(duration, durationUnit); - } - - @Override - public void responseReceiveComplete(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).responseReceivedSuccess(duration, durationUnit); - } - - @Override - public void responseReceiveFailed(int streamId, RequestType type, long duration, TimeUnit durationUnit, - Throwable cause) { - requestStats.get(type).responseReceivedFailed(duration, durationUnit); - } - - @Override - public void responseReceiveCancelled(int streamId, RequestType type, long duration, TimeUnit durationUnit) { - requestStats.get(type).responseReceivedCancelled(duration, durationUnit); - } - - @Override - public void socketClosed(long duration, TimeUnit durationUnit) { - socketClosed.increment(); - } - - @Override - public void frameWritten(int streamId, FrameType frameType) { - frameWritten.increment(); - } - - @Override - public void frameRead(int streamId, FrameType frameType) { - frameRead.increment(); - } - - @Override - public void leaseSent(int permits, int ttl) { - leaseStats.newLeaseSent(permits, ttl); - } - - @Override - public void leaseReceived(int permits, int ttl) { - leaseStats.newLeaseReceived(permits, ttl); - } - - @Override - public void errorSent(int streamId, int errorCode) { - errorStats.onErrorSent(errorCode); - } - - @Override - public void errorReceived(int streamId, int errorCode) { - errorStats.onErrorReceived(errorCode); - } -} diff --git a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/LoadBalancingClientListenerImpl.java b/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/LoadBalancingClientListenerImpl.java deleted file mode 100644 index f609fd501..000000000 --- a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/LoadBalancingClientListenerImpl.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.spectator; - -import com.netflix.spectator.api.Counter; -import com.netflix.spectator.api.Gauge; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Spectator; -import io.reactivesocket.Availability; -import io.reactivesocket.client.LoadBalancerSocketMetrics; -import io.reactivesocket.client.events.LoadBalancingClientListener; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -import static io.reactivesocket.spectator.internal.SpectatorUtil.*; - -public class LoadBalancingClientListenerImpl extends ClientEventListenerImpl - implements LoadBalancingClientListener { - - private final ConcurrentHashMap sockets; - private final ConcurrentHashMap servers; - private final Counter socketsAdded; - private final Counter socketsRemoved; - private final Counter serversAdded; - private final Counter serversRemoved; - private final Counter socketRefresh; - private final Gauge aperture; - private final Gauge socketRefreshPeriodMillis; - private final Registry registry; - private final String monitorId; - - public LoadBalancingClientListenerImpl(Registry registry, String monitorId) { - super(registry, monitorId); - this.registry = registry; - this.monitorId = monitorId; - sockets = new ConcurrentHashMap<>(); - servers = new ConcurrentHashMap<>(); - socketsAdded = registry.counter(createId(registry, "socketsAdded", monitorId)); - socketsRemoved = registry.counter(createId(registry, "socketsRemoved", monitorId)); - serversAdded = registry.counter(createId(registry, "serversAdded", monitorId)); - serversRemoved = registry.counter(createId(registry, "serversRemoved", monitorId)); - socketRefresh = registry.counter(createId(registry, "socketRefresh", monitorId)); - aperture = registry.gauge(registry.createId("aperture", "id", monitorId)); - socketRefreshPeriodMillis = registry.gauge(registry.createId("socketRefreshPeriodMillis", - "id", monitorId)); - } - - public LoadBalancingClientListenerImpl(String monitorId) { - this(Spectator.globalRegistry(), monitorId); - } - - @Override - public void socketAdded(Availability availability) { - if (availability instanceof LoadBalancerSocketMetrics) { - sockets.put(availability, new SocketStats((LoadBalancerSocketMetrics) availability)); - } - socketsAdded.increment(); - } - - @Override - public void socketRemoved(Availability availability) { - sockets.remove(availability); - socketsRemoved.increment(); - } - - @Override - public void serverAdded(Availability availability) { - servers.put(availability, availability); - registry.gauge(registry.createId("availability", "id", monitorId, "entity", "server", - "entityId", String.valueOf(availability.hashCode())), - availability, a -> a.availability()); - serversAdded.increment(); - } - - @Override - public void serverRemoved(Availability availability) { - servers.remove(availability); - serversRemoved.increment(); - } - - @Override - public void apertureChanged(int oldAperture, int newAperture) { - aperture.set(newAperture); - } - - @Override - public void socketRefreshPeriodChanged(long oldPeriod, long newPeriod, TimeUnit periodUnit) { - socketRefreshPeriodMillis.set(TimeUnit.MILLISECONDS.convert(newPeriod, periodUnit)); - } - - @Override - public void socketsRefreshCompleted(long duration, TimeUnit durationUnit) { - socketRefresh.increment(); - } - - private final class SocketStats { - - public SocketStats(LoadBalancerSocketMetrics metrics) { - registry.gauge(registry.createId("availability", "id", monitorId, "entity", "socket", - "entityId", String.valueOf(metrics.hashCode())), - metrics, m -> m.availability()); - registry.gauge(registry.createId("pendingRequests", "id", monitorId, "entity", "socket", - "entityId", String.valueOf(metrics.hashCode())), - metrics, m -> m.pending()); - registry.gauge(registry.createId("higherQuantileLatency", "id", monitorId, "entity", "socket", - "entityId", String.valueOf(metrics.hashCode())), - metrics, m -> m.higherQuantileLatency()); - registry.gauge(registry.createId("lowerQuantileLatency", "id", monitorId, "entity", "socket", - "entityId", String.valueOf(metrics.hashCode())), - metrics, m -> m.lowerQuantileLatency()); - registry.gauge(registry.createId("interArrivalTime", "id", monitorId, "entity", "socket", - "entityId", String.valueOf(metrics.hashCode())), - metrics, m -> m.interArrivalTime()); - registry.gauge(registry.createId("lastTimeUsedMillis", "id", monitorId, "entity", "socket", - "entityId", String.valueOf(metrics.hashCode())), - metrics, m -> m.lastTimeUsedMillis()); - } - } -} diff --git a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/ServerEventListenerImpl.java b/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/ServerEventListenerImpl.java deleted file mode 100644 index 4ae8afbc7..000000000 --- a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/ServerEventListenerImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.spectator; - -import com.netflix.spectator.api.Counter; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Spectator; -import io.reactivesocket.events.ServerEventListener; -import io.reactivesocket.spectator.internal.SpectatorUtil; - -public class ServerEventListenerImpl extends EventListenerImpl implements ServerEventListener { - - private final Counter socketAccepted; - - public ServerEventListenerImpl(Registry registry, String monitorId) { - super(registry, monitorId); - socketAccepted = registry.counter(SpectatorUtil.createId(registry, "socketAccepted", monitorId)); - } - - public ServerEventListenerImpl(String monitorId) { - this(Spectator.globalRegistry(), monitorId); - } - - @Override - public void socketAccepted() { - socketAccepted.increment(); - } -} diff --git a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/RequestStats.java b/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/RequestStats.java deleted file mode 100644 index cd3cc7ee5..000000000 --- a/reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/RequestStats.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2017 Netflix, Inc. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.reactivesocket.spectator.internal; - -import com.netflix.spectator.api.Counter; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Spectator; -import com.netflix.spectator.api.histogram.PercentileTimer; -import io.reactivesocket.events.EventListener.RequestType; - -import java.util.concurrent.TimeUnit; - -import static io.reactivesocket.spectator.internal.SpectatorUtil.*; - -public class RequestStats { - - private final Stats requestSentStats; - private final Stats requestReceivedStats; - private final Stats responseSentStats; - private final Stats responseReceivedStats; - - public RequestStats(Registry registry, RequestType requestType, String monitorId) { - requestSentStats = new Stats(registry, requestType, monitorId, "request", "sent"); - requestReceivedStats = new Stats(registry, requestType, monitorId, "request", "received"); - responseSentStats = new Stats(registry, requestType, monitorId, "response", "sent"); - responseReceivedStats = new Stats(registry, requestType, monitorId, "response", "received"); - } - - public RequestStats(RequestType requestType, String monitorId) { - this(Spectator.globalRegistry(), requestType, monitorId); - } - - public void requestSendStart() { - requestSentStats.start.increment(); - } - - public void requestReceivedStart() { - requestReceivedStats.start.increment(); - } - - public void requestSendSuccess(long duration, TimeUnit timeUnit) { - requestSentStats.success.increment(); - requestSentStats.successLatency.record(duration, timeUnit); - } - - public void requestReceivedSuccess(long duration, TimeUnit timeUnit) { - requestReceivedStats.success.increment(); - requestReceivedStats.successLatency.record(duration, timeUnit); - } - - public void requestSendFailed(long duration, TimeUnit timeUnit) { - requestSentStats.failure.increment(); - requestSentStats.failureLatency.record(duration, timeUnit); - } - - public void requestReceivedFailed(long duration, TimeUnit timeUnit) { - requestReceivedStats.failure.increment(); - requestReceivedStats.failureLatency.record(duration, timeUnit); - } - - public void requestSendCancelled(long duration, TimeUnit timeUnit) { - requestSentStats.cancel.increment(); - requestSentStats.cancelLatency.record(duration, timeUnit); - } - - public void requestReceivedCancelled(long duration, TimeUnit timeUnit) { - requestReceivedStats.cancel.increment(); - requestReceivedStats.cancelLatency.record(duration, timeUnit); - } - - public void responseSendStart(long requestToResponseLatency, TimeUnit timeUnit) { - responseSentStats.start.increment(); - responseSentStats.processLatency.record(requestToResponseLatency, timeUnit); - } - - public void responseReceivedStart(long requestToResponseLatency, TimeUnit timeUnit) { - responseReceivedStats.start.increment(); - responseReceivedStats.processLatency.record(requestToResponseLatency, timeUnit); - } - - public void responseSendSuccess(long duration, TimeUnit timeUnit) { - responseSentStats.success.increment(); - responseSentStats.successLatency.record(duration, timeUnit); - } - - public void responseReceivedSuccess(long duration, TimeUnit timeUnit) { - responseReceivedStats.success.increment(); - responseReceivedStats.successLatency.record(duration, timeUnit); - } - - public void responseSendFailed(long duration, TimeUnit timeUnit) { - responseSentStats.failure.increment(); - responseSentStats.failureLatency.record(duration, timeUnit); - } - - public void responseReceivedFailed(long duration, TimeUnit timeUnit) { - responseReceivedStats.failure.increment(); - responseReceivedStats.failureLatency.record(duration, timeUnit); - } - - public void responseSendCancelled(long duration, TimeUnit timeUnit) { - responseSentStats.cancel.increment(); - responseSentStats.cancelLatency.record(duration, timeUnit); - } - - public void responseReceivedCancelled(long duration, TimeUnit timeUnit) { - responseReceivedStats.cancel.increment(); - responseReceivedStats.cancelLatency.record(duration, timeUnit); - } - - private static class Stats { - - private final Counter start; - private final Counter success; - private final Counter failure; - private final Counter cancel; - private final PercentileTimer successLatency; - private final PercentileTimer failureLatency; - private final PercentileTimer cancelLatency; - private final PercentileTimer processLatency; - - public Stats(Registry registry, RequestType requestType, String monitorId, String namePrefix, - String direction) { - start = registry.counter(createId(registry, namePrefix + "Start", monitorId, - "requestType", requestType.name(), "direction", direction)); - success = registry.counter(createId(registry, namePrefix + "Success", monitorId, - "requestType", requestType.name(), "direction", direction)); - failure = registry.counter(createId(registry, namePrefix + "Failure", monitorId, - "requestType", requestType.name(), "direction", direction)); - cancel = registry.counter(createId(registry, namePrefix + "Cancel", monitorId, - "requestType", requestType.name(), "direction", direction)); - successLatency = PercentileTimer.get(registry, createId(registry, namePrefix + "Latency", monitorId, - "requestType", requestType.name(), - "direction", direction, "outcome", "success")); - failureLatency = PercentileTimer.get(registry, createId(registry, namePrefix + "Latency", monitorId, - "requestType", requestType.name(), - "direction", direction, "outcome", "failure")); - cancelLatency = PercentileTimer.get(registry, createId(registry, namePrefix + "Latency", monitorId, - "requestType", requestType.name(), - "direction", direction, "outcome", "cancel")); - processLatency = PercentileTimer.get(registry, createId(registry, namePrefix + "processingTime", monitorId, - "requestType", requestType.name(), - "direction", direction)); - } - } -} diff --git a/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/PeerConnector.java b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/PeerConnector.java index 12b5f0213..2d29d51fe 100644 --- a/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/PeerConnector.java +++ b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/PeerConnector.java @@ -18,7 +18,6 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; -import io.reactivesocket.internal.ValidatingSubscription; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,18 +79,22 @@ private LocalDuplexConnection(MonoProcessor closeNotifier, boolean client) if (receiver != null) { receiver.safeOnError(new ClosedChannelException()); } - }).subscribe(); + }) + .subscribe(); } @Override public Mono send(Publisher frames) { - return Flux.from(frames).doOnNext(frame -> { - if (peer != null) { - peer.receiveFrameFromPeer(frame); - } else { - logger.warn("Sending a frame but peer not connected. Ignoring frame: " + frame); - } - }).then(); + return Flux + .from(frames) + .doOnNext(frame -> { + if (peer != null) { + peer.receiveFrameFromPeer(frame); + } else { + logger.warn("Sending a frame but peer not connected. Ignoring frame: " + frame); + } + }) + .then(); } @Override diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ValidatingSubscription.java b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/ValidatingSubscription.java similarity index 98% rename from reactivesocket-core/src/main/java/io/reactivesocket/internal/ValidatingSubscription.java rename to reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/ValidatingSubscription.java index 9504f916f..e2496d3ef 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ValidatingSubscription.java +++ b/reactivesocket-transport-local/src/main/java/io/reactivesocket/local/internal/ValidatingSubscription.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.reactivesocket.internal; +package io.reactivesocket.local.internal; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; From 827fa7f6109b3d2e3d4c573d2b15b16d78ea97bc Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Mon, 13 Mar 2017 18:09:46 +0000 Subject: [PATCH 026/731] add working stream test (#261) * add working test * reduce timeout --- .../src/main/java/io/reactivesocket/test/ClientSetupRule.java | 2 +- .../io/reactivesocket/transport/netty/TcpClientServerTest.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java index a8b089f81..c954b1b4f 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java @@ -103,7 +103,7 @@ public void testRequestStream() { private void testStream(Function> invoker) { TestSubscriber ts = TestSubscriber.create(); - Flux publisher = invoker.apply(reactiveSocket); + Flux publisher = invoker.apply(getReactiveSocket()); publisher.take(10).subscribe(ts); await(ts); ts.assertTerminated(); diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java index 41d0cc1ab..cdeaeda69 100644 --- a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java @@ -35,7 +35,6 @@ public void testRequestResponse10() { setup.testRequestResponseN(10); } - @Test(timeout = 10000) public void testRequestResponse100() { setup.testRequestResponseN(100); @@ -46,7 +45,6 @@ public void testRequestResponse10_000() { setup.testRequestResponseN(10_000); } - @Ignore("Fix request-stream") @Test(timeout = 10000) public void testRequestStream() { setup.testRequestStream(); From c3518522449211184d114b95dd8680a9cd43b164 Mon Sep 17 00:00:00 2001 From: Ondrej Lehecka Date: Mon, 13 Mar 2017 12:25:59 -0700 Subject: [PATCH 027/731] Fix frame size field serialization (#259) * fixing frame size field * adjusting the Netty Decoder --- .../io/reactivesocket/frame/FrameHeaderFlyweight.java | 11 +++++++++-- .../frame/FrameHeaderFlyweightTest.java | 3 ++- .../transport/netty/ReactiveSocketLengthCodec.java | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java index 071acdfc8..8721b446a 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java @@ -89,8 +89,13 @@ public static int encodeFrameHeader( final FrameType frameType, final int streamId ) { + if ((frameLength & ~FRAME_LENGTH_MASK) != 0) { + throw new IllegalArgumentException("Frame length is larger than 24 bits"); + } + if (INCLUDE_FRAME_LENGTH) { - encodeLength(mutableDirectBuffer, offset + FRAME_LENGTH_FIELD_OFFSET, frameLength); + // frame length field needs to be excluded from the length + encodeLength(mutableDirectBuffer, offset + FRAME_LENGTH_FIELD_OFFSET, frameLength - FRAME_LENGTH_SIZE); } mutableDirectBuffer.putInt(offset + STREAM_ID_FIELD_OFFSET, streamId, ByteOrder.BIG_ENDIAN); @@ -251,7 +256,9 @@ static int frameLength(final DirectBuffer directBuffer, final int offset, final return externalFrameLength; } - return decodeLength(directBuffer, offset + FRAME_LENGTH_FIELD_OFFSET); + // frame length field was excluded from the length so we will add it to represent + // the entire block + return decodeLength(directBuffer, offset + FRAME_LENGTH_FIELD_OFFSET) + FRAME_LENGTH_SIZE; } private static int metadataFieldLength(DirectBuffer directBuffer, FrameType frameType, int offset, int frameLength) { diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java index 351e3007d..580d64474 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java @@ -132,7 +132,8 @@ public void wireFormat() { UnsafeBuffer expectedMutable = new UnsafeBuffer(ByteBuffer.allocate(1024)); int currentIndex = 0; // frame length - expectedMutable.putInt(currentIndex, FrameHeaderFlyweight.FRAME_HEADER_LENGTH << 8, ByteOrder.BIG_ENDIAN); + int frameLength = FrameHeaderFlyweight.FRAME_HEADER_LENGTH - FrameHeaderFlyweight.FRAME_LENGTH_SIZE; + expectedMutable.putInt(currentIndex, frameLength << 8, ByteOrder.BIG_ENDIAN); currentIndex += 3; // stream id expectedMutable.putInt(currentIndex, 5, ByteOrder.BIG_ENDIAN); diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/ReactiveSocketLengthCodec.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/ReactiveSocketLengthCodec.java index ea02933b7..4eac2bdae 100644 --- a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/ReactiveSocketLengthCodec.java +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/ReactiveSocketLengthCodec.java @@ -24,6 +24,6 @@ public class ReactiveSocketLengthCodec extends LengthFieldBasedFrameDecoder { public ReactiveSocketLengthCodec() { - super(FRAME_LENGTH_MASK, 0, FRAME_LENGTH_SIZE, -1 * FRAME_LENGTH_SIZE, 0); + super(FRAME_LENGTH_MASK, 0, FRAME_LENGTH_SIZE, 0, 0); } } From 96e076065ae134fb67767c692a7b60b94e993049 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Tue, 14 Mar 2017 13:16:33 +0000 Subject: [PATCH 028/731] flag more failure cases (#260) --- .../reactivesocket/test/ClientSetupRule.java | 32 +++++++++++++++++++ .../aeron/ClientServerTest.java | 10 ++++++ .../local/ClientServerTest.java | 18 ++++++++--- .../transport/netty/TcpClientServerTest.java | 10 ++++++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java index c954b1b4f..4b66790cf 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java @@ -79,6 +79,38 @@ public ReactiveSocket getReactiveSocket() { return reactiveSocket; } + public void testFireAndForget(int count) { + TestSubscriber ts = TestSubscriber.create(); + Flux.range(1, count) + .flatMap(i -> + getReactiveSocket() + .fireAndForget(TestUtil.utf8EncodedPayload("hello", "metadata")) + ) + .doOnError(Throwable::printStackTrace) + .subscribe(ts); + + await(ts); + ts.assertTerminated(); + ts.assertNoErrors(); + ts.assertTerminated(); + } + + public void testMetadata(int count) { + TestSubscriber ts = TestSubscriber.create(); + Flux.range(1, count) + .flatMap(i -> + getReactiveSocket() + .metadataPush(TestUtil.utf8EncodedPayload("", "metadata")) + ) + .doOnError(Throwable::printStackTrace) + .subscribe(ts); + + await(ts); + ts.assertTerminated(); + ts.assertNoErrors(); + ts.assertTerminated(); + } + public void testRequestResponseN(int count) { TestSubscriber ts = TestSubscriber.create(); Flux.range(1, count) diff --git a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/ClientServerTest.java b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/ClientServerTest.java index 0ba8bec90..e54193d87 100644 --- a/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/ClientServerTest.java +++ b/reactivesocket-transport-aeron/src/test/java/io/reactivesocket/aeron/ClientServerTest.java @@ -26,6 +26,16 @@ public class ClientServerTest { @Rule public final ClientSetupRule setup = new AeronClientSetupRule(); + @Test(timeout = 10000) + public void testFireNForget10() { + setup.testFireAndForget(10); + } + + @Test(timeout = 10000) + public void testPushMetadata10() { + setup.testMetadata(10); + } + @Test(timeout = 5000000) public void testRequestResponse1() { setup.testRequestResponseN(1); diff --git a/reactivesocket-transport-local/src/test/java/io/reactivesocket/local/ClientServerTest.java b/reactivesocket-transport-local/src/test/java/io/reactivesocket/local/ClientServerTest.java index a7e060991..831fcdf85 100644 --- a/reactivesocket-transport-local/src/test/java/io/reactivesocket/local/ClientServerTest.java +++ b/reactivesocket-transport-local/src/test/java/io/reactivesocket/local/ClientServerTest.java @@ -30,6 +30,17 @@ public class ClientServerTest { @Rule public final ClientSetupRule setup = new LocalRule(); + @Test(timeout = 10000) + public void testFireNForget10() { + setup.testFireAndForget(10); + } + + @Ignore("Push Metadata does not work as of now.") + @Test(timeout = 10000) + public void testPushMetadata10() { + setup.testMetadata(10); + } + @Test(timeout = 10000) public void testRequestResponse1() { setup.testRequestResponseN(1); @@ -40,7 +51,6 @@ public void testRequestResponse10() { setup.testRequestResponseN(10); } - @Test(timeout = 10000) public void testRequestResponse100() { setup.testRequestResponseN(100); @@ -51,7 +61,7 @@ public void testRequestResponse10_000() { setup.testRequestResponseN(10_000); } - @Ignore("Stream/Subscription does not work as of now.") + @Ignore("Stream does not work as of now.") @Test(timeout = 10000) public void testRequestStream() { setup.testRequestStream(); @@ -72,9 +82,7 @@ public LocalRule() { LocalServer localServer = LocalServer.create("test-local-server-" + uniqueNameGenerator.incrementAndGet()); return ReactiveSocketServer.create(localServer) - .start((setup, sendingSocket) -> { - return new DisabledLeaseAcceptingSocket(new TestReactiveSocket()); - }) + .start((setup, sendingSocket) -> new DisabledLeaseAcceptingSocket(new TestReactiveSocket())) .getServerAddress(); }); } diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java index cdeaeda69..ed2391b33 100644 --- a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpClientServerTest.java @@ -25,6 +25,16 @@ public class TcpClientServerTest { @Rule public final ClientSetupRule setup = new TcpClientSetupRule(); + @Test(timeout = 10000) + public void testFireNForget10() { + setup.testFireAndForget(10); + } + + @Test(timeout = 10000) + public void testPushMetadata10() { + setup.testMetadata(10); + } + @Test(timeout = 10000) public void testRequestResponse1() { setup.testRequestResponseN(1); From f5997b7c667f22eccfb81fdfcb7f4472797fe801 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Tue, 14 Mar 2017 13:42:55 +0000 Subject: [PATCH 029/731] per branch travis image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0abbf934b..e8e121443 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Others can be found in the [ReactiveSocket Github](https://github.com/ReactiveSo ## Build and Binaries - + Snapshots are available via JFrog. From 6eea0ad67816fb37af4c480307870ff2d1493c8b Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Tue, 14 Mar 2017 13:44:55 +0000 Subject: [PATCH 030/731] correct the snapshot version for the 1.0.x branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8e121443..4bdf5275f 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ repositories { } dependencies { - compile 'io.reactivesocket:reactivesocket:0.5.0-SNAPSHOT' + compile 'io.reactivesocket:reactivesocket:0.9-SNAPSHOT' } ``` From ddbb2348ec4a68aebad2905a5e2ad88be5118db3 Mon Sep 17 00:00:00 2001 From: Robert Roeser Date: Tue, 14 Mar 2017 13:25:17 -0700 Subject: [PATCH 031/731] added plugins facility for instrument client and server reactive sockets, and other for capture frames --- .../main/java/io/reactivesocket/Plugins.java | 40 ++++ .../client/DefaultReactiveSocketClient.java | 8 +- .../ClientServerInputMultiplexer.java | 28 ++- .../server/DefaultReactiveSocketServer.java | 22 +- .../spectator/SpectatorFrameCounter.java | 114 ++++++++++ .../spectator/SpectatorReactiveSocket.java | 202 ++++++++++++++++++ .../SpectatorReactiveSocketInterceptor.java | 39 ++++ .../spectator/internal/ErrorStats.java | 66 ------ .../internal/HdrHistogramPercentileTimer.java | 101 --------- .../spectator/internal/LeaseStats.java | 44 ---- .../internal/SlidingWindowHistogram.java | 109 ---------- .../spectator/internal/SpectatorUtil.java | 42 ---- .../spectator/internal/ThreadLocalAdder.java | 105 --------- .../internal/ThreadLocalAdderCounter.java | 76 ------- .../internal/SlidingWindowHistogramTest.java | 75 ------- 15 files changed, 442 insertions(+), 629 deletions(-) create mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/Plugins.java create mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/SpectatorFrameCounter.java create mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/SpectatorReactiveSocket.java create mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/SpectatorReactiveSocketInterceptor.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/ErrorStats.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/HdrHistogramPercentileTimer.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/LeaseStats.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/SlidingWindowHistogram.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/SpectatorUtil.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/ThreadLocalAdder.java delete mode 100644 reactivesocket-spectator/src/main/java/io/reactivesocket/spectator/internal/ThreadLocalAdderCounter.java delete mode 100644 reactivesocket-spectator/src/test/java/io/reactivesocket/spectator/internal/SlidingWindowHistogramTest.java diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/Plugins.java b/reactivesocket-core/src/main/java/io/reactivesocket/Plugins.java new file mode 100644 index 000000000..7100a4921 --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/Plugins.java @@ -0,0 +1,40 @@ +package io.reactivesocket; + +import io.reactivesocket.internal.ClientServerInputMultiplexer; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * + */ +public class Plugins { + public static final FrameCounter NOOP_COUNTER = type -> frame -> {}; + + private static final ReactiveSocketInterceptor NOOP_INTERCEPTOR = reactiveSocket -> reactiveSocket; + + public interface FrameCounter extends Function> {} + + public interface ReactiveSocketInterceptor extends Function {} + + public static volatile Plugins.FrameCounter FRAME_COUNTER = NOOP_COUNTER; + + public static volatile ReactiveSocketInterceptor CLIENT_REACTIVE_SOCKET_INTERCEPTOR = NOOP_INTERCEPTOR; + + public static volatile ReactiveSocketInterceptor SERVER_REACTIVE_SOCKET_INTERCEPTOR = NOOP_INTERCEPTOR; + + public static boolean emptyCounter() { + return FRAME_COUNTER == NOOP_COUNTER; + } + + public static boolean emptyClientReactiveSocketInterceptor() { + return CLIENT_REACTIVE_SOCKET_INTERCEPTOR == NOOP_INTERCEPTOR; + } + + public static boolean emptyServerReactiveSocketInterceptor() { + return SERVER_REACTIVE_SOCKET_INTERCEPTOR == NOOP_INTERCEPTOR; + } + + private Plugins() {} + +} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java index a0db5efa0..08f7dcca6 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/DefaultReactiveSocketClient.java @@ -16,6 +16,7 @@ package io.reactivesocket.client; +import io.reactivesocket.Plugins; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.transport.TransportClient; import reactor.core.publisher.Mono; @@ -30,8 +31,11 @@ public final class DefaultReactiveSocketClient implements ReactiveSocketClient { public DefaultReactiveSocketClient(TransportClient transportClient, SetupProvider setupProvider, SocketAcceptor acceptor) { - connectSource = transportClient.connect() - .then(connection -> setupProvider.accept(connection, acceptor)); + connectSource = + transportClient + .connect() + .then(connection -> setupProvider.accept(connection, acceptor)) + .map(Plugins.CLIENT_REACTIVE_SOCKET_INTERCEPTOR); } @Override diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java index e0e8fdbe1..7e00fb178 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java @@ -18,6 +18,7 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; +import io.reactivesocket.Plugins; import org.agrona.BitUtil; import org.reactivestreams.Publisher; import org.slf4j.Logger; @@ -26,6 +27,8 @@ import reactor.core.publisher.Mono; import reactor.core.publisher.MonoProcessor; +import static io.reactivesocket.Plugins.NOOP_COUNTER; + /** * {@link DuplexConnection#receive()} is a single stream on which the following type of frames arrive: *