/* * Copyright Terracotta, 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 org.ehcache.clustered.common.internal.messages; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.AppendMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.ClearMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.ClientInvalidationAck; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.ClientInvalidationAllAck; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.GetAndAppendMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.GetMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.ReplaceAtHeadMessage; import org.ehcache.clustered.common.internal.store.Chain; import org.terracotta.runnel.Struct; import org.terracotta.runnel.decoding.StructDecoder; import org.terracotta.runnel.encoding.StructEncoder; import java.nio.ByteBuffer; import java.util.UUID; import static org.ehcache.clustered.common.internal.messages.ChainCodec.CHAIN_ENCODER_FUNCTION; import static org.ehcache.clustered.common.internal.messages.ChainCodec.CHAIN_STRUCT; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_INDEX; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_NAME; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.KEY_FIELD; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.LSB_UUID_FIELD; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.MSB_UUID_FIELD; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.MSG_ID_FIELD; import static org.terracotta.runnel.StructBuilder.newStructBuilder; public class ServerStoreOpCodec { private static final Struct GET_AND_APPEND_MESSAGE_STRUCT = newStructBuilder() .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) .int64(MSG_ID_FIELD, 15) .int64(MSB_UUID_FIELD, 20) .int64(LSB_UUID_FIELD, 21) .int64(KEY_FIELD, 30) .byteBuffer("payload", 40) .build(); private static final Struct APPEND_MESSAGE_STRUCT = newStructBuilder() .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) .int64(MSG_ID_FIELD, 15) .int64(MSB_UUID_FIELD, 20) .int64(LSB_UUID_FIELD, 21) .int64(KEY_FIELD, 30) .byteBuffer("payload", 40) .build(); private static final Struct REPLACE_MESSAGE_STRUCT = newStructBuilder() .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) .int64(MSG_ID_FIELD, 15) .int64(MSB_UUID_FIELD, 20) .int64(LSB_UUID_FIELD, 21) .int64(KEY_FIELD, 30) .struct("expect", 40, CHAIN_STRUCT) .struct("update", 50, CHAIN_STRUCT) .build(); private static final Struct CLIENT_INVALIDATION_ACK_MESSAGE_STRUCT = newStructBuilder() .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) .int64(MSG_ID_FIELD, 15) .int64(KEY_FIELD, 20) .int32("invalidationId", 30) .build(); private static final Struct CLIENT_INVALIDATION_ALL_ACK_MESSAGE_STRUCT = newStructBuilder() .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) .int64(MSG_ID_FIELD, 15) .int32("invalidationId", 40) .build(); private static final Struct CLEAR_MESSAGE_STRUCT = newStructBuilder() .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) .int64(MSG_ID_FIELD, 15) .int64(MSB_UUID_FIELD, 20) .int64(LSB_UUID_FIELD, 21) .build(); private static final Struct GET_MESSAGE_STRUCT = newStructBuilder() .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) .int64(MSG_ID_FIELD, 15) .int64(KEY_FIELD, 30) .build(); private final MessageCodecUtils messageCodecUtils = new MessageCodecUtils(); public byte[] encode(ServerStoreOpMessage message) { StructEncoder<Void> encoder; switch (message.getMessageType()) { case GET_STORE: GetMessage getMessage = (GetMessage) message; encoder = GET_MESSAGE_STRUCT.encoder(); return encoder .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) .int64(MSG_ID_FIELD, message.getId()) .int64(KEY_FIELD, getMessage.getKey()) .encode() .array(); case APPEND: AppendMessage appendMessage = (AppendMessage) message; encoder = APPEND_MESSAGE_STRUCT.encoder(); messageCodecUtils.encodeMandatoryFields(encoder, message); return encoder .int64(KEY_FIELD, appendMessage.getKey()) .byteBuffer("payload", appendMessage.getPayload()) .encode() .array(); case GET_AND_APPEND: GetAndAppendMessage getAndAppendMessage = (GetAndAppendMessage) message; encoder = GET_AND_APPEND_MESSAGE_STRUCT.encoder(); messageCodecUtils.encodeMandatoryFields(encoder, message); return encoder .int64(KEY_FIELD, getAndAppendMessage.getKey()) .byteBuffer("payload", getAndAppendMessage.getPayload()) .encode() .array(); case REPLACE: final ReplaceAtHeadMessage replaceAtHeadMessage = (ReplaceAtHeadMessage) message; encoder = REPLACE_MESSAGE_STRUCT.encoder(); messageCodecUtils.encodeMandatoryFields(encoder, message); return encoder .int64(KEY_FIELD, replaceAtHeadMessage.getKey()) .struct("expect", replaceAtHeadMessage.getExpect(), CHAIN_ENCODER_FUNCTION) .struct("update", replaceAtHeadMessage.getUpdate(), CHAIN_ENCODER_FUNCTION) .encode() .array(); case CLIENT_INVALIDATION_ACK: ClientInvalidationAck clientInvalidationAckMessage = (ClientInvalidationAck) message; encoder = CLIENT_INVALIDATION_ACK_MESSAGE_STRUCT.encoder(); return encoder .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) .int64(MSG_ID_FIELD, message.getId()) .int64(KEY_FIELD, clientInvalidationAckMessage.getKey()) .int32("invalidationId", clientInvalidationAckMessage.getInvalidationId()) .encode() .array(); case CLIENT_INVALIDATION_ALL_ACK: ClientInvalidationAllAck clientInvalidationAllAckMessage = (ClientInvalidationAllAck) message; encoder = CLIENT_INVALIDATION_ALL_ACK_MESSAGE_STRUCT.encoder(); return encoder .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) .int64(MSG_ID_FIELD, message.getId()) .int32("invalidationId", clientInvalidationAllAckMessage.getInvalidationId()) .encode().array(); case CLEAR: encoder = CLEAR_MESSAGE_STRUCT.encoder(); messageCodecUtils.encodeMandatoryFields(encoder, message); return encoder .encode() .array(); default: throw new RuntimeException("Unhandled message operation : " + message.getMessageType()); } } public EhcacheEntityMessage decode(EhcacheMessageType opCode, ByteBuffer messageBuffer) { StructDecoder<Void> decoder; switch (opCode) { case GET_STORE: { decoder = GET_MESSAGE_STRUCT.decoder(messageBuffer); Long msgId = decoder.int64(MSG_ID_FIELD); Long key = decoder.int64(KEY_FIELD); GetMessage message = new GetMessage(key); message.setId(msgId); return message; } case GET_AND_APPEND: { decoder = GET_AND_APPEND_MESSAGE_STRUCT.decoder(messageBuffer); Long msgId = decoder.int64(MSG_ID_FIELD); UUID uuid = messageCodecUtils.decodeUUID(decoder); Long key = decoder.int64(KEY_FIELD); ByteBuffer payload = decoder.byteBuffer("payload"); GetAndAppendMessage message = new GetAndAppendMessage(key, payload, uuid); message.setId(msgId); return message; } case APPEND: { decoder = APPEND_MESSAGE_STRUCT.decoder(messageBuffer); Long msgId = decoder.int64(MSG_ID_FIELD); UUID uuid = messageCodecUtils.decodeUUID(decoder); Long key = decoder.int64(KEY_FIELD); ByteBuffer payload = decoder.byteBuffer("payload"); AppendMessage message = new AppendMessage(key, payload, uuid); message.setId(msgId); return message; } case REPLACE: { decoder = REPLACE_MESSAGE_STRUCT.decoder(messageBuffer); Long msgId = decoder.int64(MSG_ID_FIELD); UUID uuid = messageCodecUtils.decodeUUID(decoder); Long key = decoder.int64(KEY_FIELD); Chain expect = ChainCodec.decode(decoder.struct("expect")); Chain update = ChainCodec.decode(decoder.struct("update")); ReplaceAtHeadMessage message = new ReplaceAtHeadMessage(key, expect, update, uuid); message.setId(msgId); return message; } case CLIENT_INVALIDATION_ACK: { decoder = CLIENT_INVALIDATION_ACK_MESSAGE_STRUCT.decoder(messageBuffer); Long msgId = decoder.int64(MSG_ID_FIELD); Long key = decoder.int64(KEY_FIELD); Integer invalidationId = decoder.int32("invalidationId"); ClientInvalidationAck message = new ClientInvalidationAck(key, invalidationId); message.setId(msgId); return message; } case CLIENT_INVALIDATION_ALL_ACK: { decoder = CLIENT_INVALIDATION_ALL_ACK_MESSAGE_STRUCT .decoder(messageBuffer); Long msgId = decoder.int64(MSG_ID_FIELD); Integer invalidationId = decoder.int32("invalidationId"); ClientInvalidationAllAck message = new ClientInvalidationAllAck(invalidationId); message.setId(msgId); return message; } case CLEAR: { decoder = CLEAR_MESSAGE_STRUCT.decoder(messageBuffer); Long msgId = decoder.int64(MSG_ID_FIELD); UUID uuid = messageCodecUtils.decodeUUID(decoder); ClearMessage message = new ClearMessage(uuid); message.setId(msgId); return message; } default: throw new RuntimeException("Unhandled message operation : " + opCode); } } }