/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * 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 com.hazelcast.client.protocol; import com.hazelcast.client.impl.protocol.ClientMessage; import com.hazelcast.client.impl.protocol.exception.MaxMessageSizeExceeded; import com.hazelcast.client.impl.protocol.util.ClientProtocolBuffer; import com.hazelcast.client.impl.protocol.util.ParameterUtil; import com.hazelcast.client.impl.protocol.util.SafeBuffer; import com.hazelcast.nio.Bits; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; /** * ClientMessage Tests of Flyweight functionality */ @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class ClientMessageTest { private static final String DEFAULT_ENCODING = "UTF8"; private static final String VAR_DATA_STR_1 = "abcdef"; private static final byte[] BYTE_DATA = VAR_DATA_STR_1.getBytes(); @Before public void setUp() { } @After public void tearDown() { } @Test public void shouldEncodeClientMessageCorrectly() { ByteBuffer byteBuffer = ByteBuffer.allocate(512); SafeBuffer safeBuffer = new SafeBuffer(byteBuffer.array()); ClientMessage cmEncode = TestClientMessage.createForEncode(safeBuffer, 0); cmEncode.setMessageType(0x1122).setVersion((short) 0xEF).addFlag(ClientMessage.BEGIN_AND_END_FLAGS) .setCorrelationId(0x1234567812345678L) .setPartitionId(0x11223344); // little endian //FRAME LENGTH assertThat(byteBuffer.get(0), is((byte) ClientMessage.HEADER_SIZE)); assertThat(byteBuffer.get(1), is((byte) 0)); assertThat(byteBuffer.get(2), is((byte) 0)); assertThat(byteBuffer.get(3), is((byte) 0)); //VERSION assertThat(byteBuffer.get(4), is((byte) 0xEF)); //FLAGS assertThat(byteBuffer.get(5), is((byte) 0xC0)); //TYPE assertThat(byteBuffer.get(6), is((byte) 0x22)); assertThat(byteBuffer.get(7), is((byte) 0x11)); //setCorrelationId assertThat(byteBuffer.get(8), is((byte) 0x78)); assertThat(byteBuffer.get(9), is((byte) 0x56)); assertThat(byteBuffer.get(10), is((byte) 0x34)); assertThat(byteBuffer.get(11), is((byte) 0x12)); assertThat(byteBuffer.get(12), is((byte) 0x78)); assertThat(byteBuffer.get(13), is((byte) 0x56)); assertThat(byteBuffer.get(14), is((byte) 0x34)); assertThat(byteBuffer.get(15), is((byte) 0x12)); //setPartitionId assertThat(byteBuffer.get(16), is((byte) 0x44)); assertThat(byteBuffer.get(17), is((byte) 0x33)); assertThat(byteBuffer.get(18), is((byte) 0x22)); assertThat(byteBuffer.get(19), is((byte) 0x11)); //data offset assertThat(byteBuffer.get(20), is((byte) ClientMessage.HEADER_SIZE)); assertThat(byteBuffer.get(21), is((byte) 0x00)); } @Test public void shouldEncodeAndDecodeClientMessageCorrectly() { SafeBuffer byteBuffer = new SafeBuffer(new byte[512]); ClientMessage cmEncode = TestClientMessage.createForEncode(byteBuffer, 0); cmEncode.setMessageType(7) .setVersion((short) 3) .addFlag(ClientMessage.BEGIN_AND_END_FLAGS) .setCorrelationId(66).setPartitionId(77); ClientMessage cmDecode = ClientMessage.createForDecode(byteBuffer, 0); assertEquals(7, cmDecode.getMessageType()); assertEquals(3, cmDecode.getVersion()); assertEquals(ClientMessage.BEGIN_AND_END_FLAGS, cmDecode.getFlags()); assertEquals(66, cmDecode.getCorrelationId()); assertEquals(77, cmDecode.getPartitionId()); assertEquals(ClientMessage.HEADER_SIZE, cmDecode.getFrameLength()); } @Test public void shouldEncodeAndDecodeClientMessageCorrectly_withPayLoadData() throws UnsupportedEncodingException { SafeBuffer byteBuffer = new SafeBuffer(new byte[1024]); ClientMessage cmEncode = TestClientMessage.createForEncode(byteBuffer, 0); cmEncode.setMessageType(7) .setVersion((short) 3) .addFlag(ClientMessage.BEGIN_AND_END_FLAGS) .setCorrelationId(66).setPartitionId(77); final byte[] data1 = VAR_DATA_STR_1.getBytes(DEFAULT_ENCODING); final int calculatedFrameSize = ClientMessage.HEADER_SIZE + ParameterUtil.calculateDataSize(data1); cmEncode.set(data1); cmEncode.updateFrameLength(); ClientMessage cmDecode = ClientMessage.createForDecode(byteBuffer, 0); byte[] cmDecodeVarData1 = cmDecode.getByteArray(); assertEquals(calculatedFrameSize, cmDecode.getFrameLength()); assertArrayEquals(cmDecodeVarData1, data1); } @Test public void shouldEncodeAndDecodeClientMessageCorrectly_withPayLoadData_fromOffset() throws UnsupportedEncodingException { SafeBuffer byteBuffer = new SafeBuffer(new byte[150]); int offset = 100; ClientMessage cmEncode = TestClientMessage.createForEncode(byteBuffer, offset); cmEncode.setMessageType(7) .setVersion((short) 3) .addFlag(ClientMessage.BEGIN_AND_END_FLAGS) .setCorrelationId(66).setPartitionId(77); byte[] bytes = VAR_DATA_STR_1.getBytes(); final int calculatedFrameSize = ClientMessage.HEADER_SIZE + Bits.INT_SIZE_IN_BYTES + ParameterUtil.calculateDataSize(bytes); cmEncode.set(1); cmEncode.set(bytes); cmEncode.updateFrameLength(); ClientMessage cmDecode = ClientMessage.createForDecode(byteBuffer, offset); assertEquals(1, cmDecode.getInt()); assertArrayEquals(bytes, cmDecode.getByteArray()); assertEquals(calculatedFrameSize, cmDecode.getFrameLength()); } @Test public void shouldEncodeWithNewVersionAndDecodeWithOldVersionCorrectly_withPayLoadData() throws UnsupportedEncodingException { SafeBuffer byteBuffer = new SafeBuffer(new byte[1024]); FutureClientMessage cmEncode = new FutureClientMessage(); cmEncode.wrapForEncode(byteBuffer, 0); cmEncode.theNewField(999) .setMessageType(7).setVersion((short) 3) .addFlag(ClientMessage.BEGIN_AND_END_FLAGS) .setCorrelationId(66).setPartitionId(77); final int calculatedFrameSize = FutureClientMessage.THE_NEW_HEADER_SIZE + ParameterUtil.calculateDataSize(BYTE_DATA); cmEncode.set(BYTE_DATA); cmEncode.updateFrameLength(); ClientMessage cmDecode = ClientMessage.createForDecode(byteBuffer, 0); final byte[] cmDecodeVarData1 = cmDecode.getByteArray(); assertEquals(7, cmDecode.getMessageType()); assertEquals(3, cmDecode.getVersion()); assertEquals(ClientMessage.BEGIN_AND_END_FLAGS, cmDecode.getFlags()); assertEquals(66, cmDecode.getCorrelationId()); assertEquals(77, cmDecode.getPartitionId()); assertEquals(calculatedFrameSize, cmDecode.getFrameLength()); assertArrayEquals(cmDecodeVarData1, BYTE_DATA); } @Test public void shouldEncodeWithOldVersionAndDecodeWithNewVersionCorrectly_withPayLoadData() throws UnsupportedEncodingException { SafeBuffer byteBuffer = new SafeBuffer(new byte[1024]); ClientMessage cmEncode = TestClientMessage.createForEncode(byteBuffer, 0); cmEncode.setMessageType(7).setVersion((short) 3) .addFlag(ClientMessage.BEGIN_AND_END_FLAGS) .setCorrelationId(66).setPartitionId(77); final int calculatedFrameSize = ClientMessage.HEADER_SIZE + ParameterUtil.calculateDataSize(BYTE_DATA); cmEncode.set(BYTE_DATA); cmEncode.updateFrameLength(); ClientMessage cmDecode = FutureClientMessage.createForDecode(byteBuffer, 0); final byte[] cmDecodeVarData1 = cmDecode.getByteArray(); assertEquals(7, cmDecode.getMessageType()); assertEquals(3, cmDecode.getVersion()); assertEquals(ClientMessage.BEGIN_AND_END_FLAGS, cmDecode.getFlags()); assertEquals(66, cmDecode.getCorrelationId()); assertEquals(77, cmDecode.getPartitionId()); assertEquals(calculatedFrameSize, cmDecode.getFrameLength()); assertArrayEquals(cmDecodeVarData1, BYTE_DATA); } @Test public void shouldEncodeAndDecodeClientMessageCorrectly_withPayLoadData_multipleMessages() throws UnsupportedEncodingException { SafeBuffer byteBuffer = new SafeBuffer(new byte[1024]); ClientMessage cmEncode = TestClientMessage.createForEncode(byteBuffer, 0); cmEncode.setMessageType(7).setVersion((short) 3).addFlag(ClientMessage.BEGIN_AND_END_FLAGS) .setCorrelationId(1).setPartitionId(77); cmEncode.set(BYTE_DATA); cmEncode.updateFrameLength(); final int calculatedFrame1Size = ClientMessage.HEADER_SIZE + ParameterUtil.calculateDataSize(BYTE_DATA); final int nexMessageOffset = cmEncode.getFrameLength(); ClientMessage cmEncode2 = TestClientMessage.createForEncode(byteBuffer, nexMessageOffset); cmEncode2.setMessageType(7).setVersion((short) 3).addFlag(ClientMessage.BEGIN_AND_END_FLAGS) .setCorrelationId(2).setPartitionId(77); cmEncode2.set(BYTE_DATA); cmEncode2.updateFrameLength(); final int calculatedFrame2Size = ClientMessage.HEADER_SIZE + ParameterUtil.calculateDataSize(BYTE_DATA); ClientMessage cmDecode1 = ClientMessage.createForDecode(byteBuffer, 0); final byte[] cmDecodeVarData = cmDecode1.getByteArray(); assertEquals(7, cmDecode1.getMessageType()); assertEquals(3, cmDecode1.getVersion()); assertEquals(ClientMessage.BEGIN_AND_END_FLAGS, cmDecode1.getFlags()); assertEquals(1, cmDecode1.getCorrelationId()); assertEquals(77, cmDecode1.getPartitionId()); assertEquals(calculatedFrame1Size, cmDecode1.getFrameLength()); assertArrayEquals(cmDecodeVarData, BYTE_DATA); ClientMessage cmDecode2 = ClientMessage.createForDecode(byteBuffer, cmDecode1.getFrameLength()); byte[] cmDecodeVarData2 = cmDecode2.getByteArray(); assertEquals(7, cmDecode2.getMessageType()); assertEquals(3, cmDecode2.getVersion()); assertEquals(ClientMessage.BEGIN_AND_END_FLAGS, cmDecode2.getFlags()); assertEquals(2, cmDecode2.getCorrelationId()); assertEquals(77, cmDecode2.getPartitionId()); assertEquals(calculatedFrame2Size, cmDecode2.getFrameLength()); assertArrayEquals(cmDecodeVarData2, BYTE_DATA); } private static class FutureClientMessage extends TestClientMessage { private static final int THE_NEW_FIELD_OFFSET = HEADER_SIZE + Bits.SHORT_SIZE_IN_BYTES; private static final int THE_NEW_HEADER_SIZE = HEADER_SIZE + Bits.INT_SIZE_IN_BYTES; @Override protected void wrapForEncode(ClientProtocolBuffer buffer, int offset) { super.wrap(buffer, offset); setDataOffset(THE_NEW_HEADER_SIZE); setFrameLength(THE_NEW_HEADER_SIZE); index(getDataOffset()); } public int theNewField() { return (int) uint32Get(THE_NEW_FIELD_OFFSET); } public FutureClientMessage theNewField(int value) { uint32Put(THE_NEW_FIELD_OFFSET, value); return this; } } private static class TestClientMessage extends ClientMessage { @Override public ClientMessage setMessageType(int type) { return super.setMessageType(type); } } @Test public void test_empty_toString() { ClientMessage.create().toString(); } @Test(expected = IndexOutOfBoundsException.class) public void test_byteArray_constructor_withSmallBuffer() { ClientMessage.createForEncode(new SafeBuffer(new byte[10]), 1); } @Test public void test_byteArray_constructor_withHeaderSizeBuffer() { ClientMessage.createForEncode(new SafeBuffer(new byte[ClientMessage.HEADER_SIZE]), 0); } @Test public void test_byteArray_constructor_withLargeBuffer() { ClientMessage.createForEncode(new SafeBuffer(new byte[100]), 10); } @Test(expected = IndexOutOfBoundsException.class) public void test_MutableDirectBuffer_constructor_withSmallBuffer() { SafeBuffer buffer = new SafeBuffer(new byte[10]); ClientMessage.createForEncode(buffer, 1); } @Test public void test_MutableDirectBuffer_constructor_withHeaderSizeBuffer() { SafeBuffer buffer = new SafeBuffer(new byte[ClientMessage.HEADER_SIZE]); ClientMessage.createForEncode(buffer, 0); } @Test public void test_MutableDirectBuffer_constructor_withLargeBuffer() { SafeBuffer buffer = new SafeBuffer(new byte[100]); ClientMessage.createForEncode(buffer, 10); } @Test(expected = IndexOutOfBoundsException.class) public void test_wrapForEncode_withSmallBuffer() { ClientMessage.createForEncode(new SafeBuffer(new byte[10]), 1); } @Test public void test_wrapForEncode_withHeaderSizeBuffer() { ClientMessage.createForEncode(new SafeBuffer(new byte[ClientMessage.HEADER_SIZE]), 0); } @Test public void test_wrapForEncode_withLargeBuffer() { ClientMessage.createForEncode(new SafeBuffer(new byte[100]), 10); } @Test(expected = IndexOutOfBoundsException.class) public void test_wrapForDecode_withSmallBuffer() { ClientMessage.createForDecode(new SafeBuffer(new byte[10]), 1); } @Test public void test_wrapForDecode_withHeaderSizeBuffer() { ClientMessage.createForDecode(new SafeBuffer(new byte[ClientMessage.HEADER_SIZE]), 0); } @Test public void test_wrapForDecode_withLargeBuffer() { ClientMessage.createForDecode(new SafeBuffer(new byte[100]), 10); } @Test public void testUnsignedFields() throws IOException { ClientProtocolBuffer buffer = new SafeBuffer(new byte[22]); ClientMessage cmEncode = ClientMessage.createForEncode(buffer, 0); cmEncode.setVersion((short) (Byte.MAX_VALUE + 10)); cmEncode.setMessageType(Short.MAX_VALUE + 10); cmEncode.setDataOffset((int) Short.MAX_VALUE + 10); cmEncode.setCorrelationId(Integer.MAX_VALUE + 10); ClientMessage cmDecode = ClientMessage.createForDecode(buffer, 0); assertEquals(Byte.MAX_VALUE + 10, cmDecode.getVersion()); assertEquals(Short.MAX_VALUE + 10, cmDecode.getMessageType()); assertEquals((int) Short.MAX_VALUE + 10, cmDecode.getDataOffset()); } @Test(expected = MaxMessageSizeExceeded.class) public void testMessageSizeOverflow() throws Exception { ClientMessage.findSuitableMessageSize(Integer.MAX_VALUE << 1); } }