Repository: geode Updated Branches: refs/heads/develop e02be316e -> aef891296
GEODE-2998: Add remove operation Signed-off-by: Alexander Murmann <amurm...@pivotal.io> Signed-off-by: Matt Kalinowski <mkalinow...@pivotal.io> This closes #624 Project: http://git-wip-us.apache.org/repos/asf/geode/repo Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/aef89129 Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/aef89129 Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/aef89129 Branch: refs/heads/develop Commit: aef89129626d9072dfc89e8acc038ecad1793c26 Parents: e02be31 Author: Brian Rowe <br...@pivotal.io> Authored: Thu Jul 6 16:36:51 2017 -0700 Committer: Hitesh Khamesra <hkhame...@pivotal.io> Committed: Tue Jul 11 14:18:39 2017 -0700 ---------------------------------------------------------------------- .../protobuf/ProtobufStreamProcessor.java | 4 + .../RemoveRequestOperationHandler.java | 65 +++++++++ .../utilities/ProtobufRequestUtilities.java | 14 ++ .../utilities/ProtobufResponseUtilities.java | 10 ++ geode-protobuf/src/main/proto/region_API.proto | 1 - .../RoundTripCacheConnectionJUnitTest.java | 20 +++ .../RemoveRequestOperationHandlerJUnitTest.java | 142 +++++++++++++++++++ 7 files changed, 255 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/geode/blob/aef89129/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java ---------------------------------------------------------------------- diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java index ebd5c6a..7146392 100644 --- a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java +++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java @@ -24,6 +24,7 @@ import org.apache.geode.protocol.operations.registry.exception.OperationHandlerN import org.apache.geode.protocol.protobuf.operations.GetRegionNamesRequestOperationHandler; import org.apache.geode.protocol.protobuf.operations.GetRequestOperationHandler; import org.apache.geode.protocol.protobuf.operations.PutRequestOperationHandler; +import org.apache.geode.protocol.protobuf.operations.RemoveRequestOperationHandler; import org.apache.geode.protocol.protobuf.serializer.ProtobufProtocolSerializer; import org.apache.geode.protocol.protobuf.utilities.ProtobufUtilities; import org.apache.geode.serialization.registry.exception.CodecAlreadyRegisteredForTypeException; @@ -63,6 +64,9 @@ public class ProtobufStreamProcessor implements ClientProtocolMessageHandler { registry.registerOperationHandlerForOperationId( ClientProtocol.Request.RequestAPICase.GETREGIONNAMESREQUEST.getNumber(), new GetRegionNamesRequestOperationHandler()); + registry.registerOperationHandlerForOperationId( + ClientProtocol.Request.RequestAPICase.REMOVEREQUEST.getNumber(), + new RemoveRequestOperationHandler()); } public void processOneMessage(InputStream inputStream, OutputStream outputStream, Cache cache) http://git-wip-us.apache.org/repos/asf/geode/blob/aef89129/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/operations/RemoveRequestOperationHandler.java ---------------------------------------------------------------------- diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/operations/RemoveRequestOperationHandler.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/operations/RemoveRequestOperationHandler.java new file mode 100644 index 0000000..e1fef85 --- /dev/null +++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/operations/RemoveRequestOperationHandler.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.protocol.protobuf.operations; + +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.Region; +import org.apache.geode.protocol.operations.OperationHandler; +import org.apache.geode.protocol.protobuf.BasicTypes; +import org.apache.geode.protocol.protobuf.ClientProtocol; +import org.apache.geode.protocol.protobuf.RegionAPI; +import org.apache.geode.protocol.protobuf.utilities.ProtobufResponseUtilities; +import org.apache.geode.protocol.protobuf.utilities.ProtobufUtilities; +import org.apache.geode.serialization.SerializationService; +import org.apache.geode.serialization.exception.UnsupportedEncodingTypeException; +import org.apache.geode.serialization.registry.exception.CodecNotRegisteredForTypeException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class RemoveRequestOperationHandler + implements OperationHandler<ClientProtocol.Request, ClientProtocol.Response> { + private static Logger logger = LogManager.getLogger(); + + @Override + public ClientProtocol.Response process(SerializationService serializationService, + ClientProtocol.Request request, Cache cache) { + if (request.getRequestAPICase() != ClientProtocol.Request.RequestAPICase.REMOVEREQUEST) { + return ProtobufResponseUtilities.createAndLogErrorResponse(false, false, + "Improperly formatted get request message.", logger, null); + } + RegionAPI.RemoveRequest removeRequest = request.getRemoveRequest(); + + String regionName = removeRequest.getRegionName(); + Region region = cache.getRegion(regionName); + if (region == null) { + return ProtobufResponseUtilities.createErrorResponse(false, false, "Region not found"); + } + + try { + Object decodedKey = + ProtobufUtilities.decodeValue(serializationService, removeRequest.getKey()); + region.remove(decodedKey); + + return ProtobufResponseUtilities.createRemoveResponse(); + } catch (UnsupportedEncodingTypeException ex) { + // can be thrown by encoding or decoding. + return ProtobufResponseUtilities.createAndLogErrorResponse(false, false, + "Encoding not supported.", logger, ex); + } catch (CodecNotRegisteredForTypeException ex) { + return ProtobufResponseUtilities.createAndLogErrorResponse(true, false, + "Codec error in protobuf deserialization.", logger, ex); + } + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/aef89129/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufRequestUtilities.java ---------------------------------------------------------------------- diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufRequestUtilities.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufRequestUtilities.java index b96f478..b246a50 100644 --- a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufRequestUtilities.java +++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufRequestUtilities.java @@ -38,6 +38,20 @@ public abstract class ProtobufRequestUtilities { } /** + * Creates a request object containing a RegionAPI.RemoveRequest + * + * @param regionName - Name of the region being deleted from + * @param key - Encoded key, see createEncodedValue in {@link ProtobufRequestUtilities} + * @return Request object containing the passed params. + */ + public static ClientProtocol.Request createRemoveRequest(String regionName, + BasicTypes.EncodedValue key) { + RegionAPI.RemoveRequest removeRequest = + RegionAPI.RemoveRequest.newBuilder().setRegionName(regionName).setKey(key).build(); + return ClientProtocol.Request.newBuilder().setRemoveRequest(removeRequest).build(); + } + + /** * Creates a request object containing a RegionAPI.GetRegionNamesRequest * * @return Request object for a getRegionNames operation http://git-wip-us.apache.org/repos/asf/geode/blob/aef89129/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufResponseUtilities.java ---------------------------------------------------------------------- diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufResponseUtilities.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufResponseUtilities.java index 2114fdb..d6ef278 100644 --- a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufResponseUtilities.java +++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/utilities/ProtobufResponseUtilities.java @@ -80,6 +80,16 @@ public abstract class ProtobufResponseUtilities { } /** + * This creates a response object containing a RegionAPI.RemoveResponse + * + * @return A response indicating the entry with the passed key was removed + */ + public static ClientProtocol.Response createRemoveResponse() { + RegionAPI.RemoveResponse removeResponse = RegionAPI.RemoveResponse.newBuilder().build(); + return ClientProtocol.Response.newBuilder().setRemoveResponse(removeResponse).build(); + } + + /** * This creates a response object containing a RegionAPI.GetResponse * * @return A response indicating a failure to find a requested key or value http://git-wip-us.apache.org/repos/asf/geode/blob/aef89129/geode-protobuf/src/main/proto/region_API.proto ---------------------------------------------------------------------- diff --git a/geode-protobuf/src/main/proto/region_API.proto b/geode-protobuf/src/main/proto/region_API.proto index 40c124a..3108cb7 100644 --- a/geode-protobuf/src/main/proto/region_API.proto +++ b/geode-protobuf/src/main/proto/region_API.proto @@ -69,7 +69,6 @@ message RemoveRequest { } message RemoveResponse { - bool success = 1; } message RemoveAllRequest { http://git-wip-us.apache.org/repos/asf/geode/blob/aef89129/geode-protobuf/src/test/java/org/apache/geode/protocol/RoundTripCacheConnectionJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-protobuf/src/test/java/org/apache/geode/protocol/RoundTripCacheConnectionJUnitTest.java b/geode-protobuf/src/test/java/org/apache/geode/protocol/RoundTripCacheConnectionJUnitTest.java index 31a8736..3838648 100644 --- a/geode-protobuf/src/test/java/org/apache/geode/protocol/RoundTripCacheConnectionJUnitTest.java +++ b/geode-protobuf/src/test/java/org/apache/geode/protocol/RoundTripCacheConnectionJUnitTest.java @@ -50,6 +50,7 @@ import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * Test that switching on the header byte makes instances of @@ -62,6 +63,7 @@ public class RoundTripCacheConnectionJUnitTest { public static final String TEST_REGION = "testRegion"; public static final int TEST_PUT_CORRELATION_ID = 574; public static final int TEST_GET_CORRELATION_ID = 68451; + public static final int TEST_REMOVE_CORRELATION_ID = 51; private Cache cache; private int cacheServerPort; @@ -111,6 +113,13 @@ public class RoundTripCacheConnectionJUnitTest { TEST_KEY, TEST_REGION, ProtobufUtilities.createMessageHeader(TEST_GET_CORRELATION_ID)); protobufProtocolSerializer.serialize(getMessage, outputStream); validateGetResponse(socket, protobufProtocolSerializer); + + ClientProtocol.Message removeMessage = ProtobufUtilities.createProtobufRequest( + ProtobufUtilities.createMessageHeader(TEST_REMOVE_CORRELATION_ID), + ProtobufRequestUtilities.createRemoveRequest(TEST_REGION, + ProtobufUtilities.createEncodedValue(serializationService, TEST_KEY))); + protobufProtocolSerializer.serialize(removeMessage, outputStream); + validateRemoveResponse(socket, protobufProtocolSerializer); } @Test @@ -206,4 +215,15 @@ public class RoundTripCacheConnectionJUnitTest { assertEquals(1, getRegionsResponse.getRegionsCount()); assertEquals(TEST_REGION, getRegionsResponse.getRegions(0)); } + + private void validateRemoveResponse(Socket socket, + ProtobufProtocolSerializer protobufProtocolSerializer) throws Exception { + ClientProtocol.Message message = + protobufProtocolSerializer.deserialize(socket.getInputStream()); + assertEquals(TEST_REMOVE_CORRELATION_ID, message.getMessageHeader().getCorrelationId()); + assertEquals(ClientProtocol.Message.MessageTypeCase.RESPONSE, message.getMessageTypeCase()); + ClientProtocol.Response response = message.getResponse(); + assertEquals(ClientProtocol.Response.ResponseAPICase.REMOVERESPONSE, + response.getResponseAPICase()); + } } http://git-wip-us.apache.org/repos/asf/geode/blob/aef89129/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/operations/RemoveRequestOperationHandlerJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/operations/RemoveRequestOperationHandlerJUnitTest.java b/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/operations/RemoveRequestOperationHandlerJUnitTest.java new file mode 100644 index 0000000..c66d2f2 --- /dev/null +++ b/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/operations/RemoveRequestOperationHandlerJUnitTest.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.protocol.protobuf.operations; + +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.Region; +import org.apache.geode.protocol.protobuf.BasicTypes; +import org.apache.geode.protocol.protobuf.ClientProtocol; +import org.apache.geode.protocol.protobuf.RegionAPI; +import org.apache.geode.protocol.protobuf.utilities.ProtobufRequestUtilities; +import org.apache.geode.protocol.protobuf.utilities.ProtobufUtilities; +import org.apache.geode.serialization.SerializationService; +import org.apache.geode.serialization.codec.StringCodec; +import org.apache.geode.serialization.exception.UnsupportedEncodingTypeException; +import org.apache.geode.serialization.registry.exception.CodecAlreadyRegisteredForTypeException; +import org.apache.geode.serialization.registry.exception.CodecNotRegisteredForTypeException; +import org.apache.geode.test.dunit.Assert; +import org.apache.geode.test.junit.categories.UnitTest; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.nio.charset.Charset; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@Category(UnitTest.class) +public class RemoveRequestOperationHandlerJUnitTest { + public static final String TEST_KEY = "my key"; + public static final String TEST_VALUE = "my value"; + public static final String TEST_REGION = "test region"; + public static final String MISSING_REGION = "missing region"; + public static final String MISSING_KEY = "missing key"; + public Cache cacheStub; + public SerializationService serializationServiceStub; + private RemoveRequestOperationHandler operationHandler; + private StringCodec stringDecoder; + private Region regionStub; + + @Before + public void setUp() throws Exception { + serializationServiceStub = mock(SerializationService.class); + when(serializationServiceStub.decode(BasicTypes.EncodingType.STRING, + TEST_KEY.getBytes(Charset.forName("UTF-8")))).thenReturn(TEST_KEY); + when(serializationServiceStub.encode(BasicTypes.EncodingType.STRING, TEST_VALUE)) + .thenReturn(TEST_VALUE.getBytes(Charset.forName("UTF-8"))); + when(serializationServiceStub.encode(BasicTypes.EncodingType.STRING, TEST_KEY)) + .thenReturn(TEST_KEY.getBytes(Charset.forName("UTF-8"))); + when(serializationServiceStub.encode(BasicTypes.EncodingType.STRING, MISSING_KEY)) + .thenReturn(MISSING_KEY.getBytes(Charset.forName("UTF-8"))); + + regionStub = mock(Region.class); + when(regionStub.remove(TEST_KEY)).thenReturn(TEST_VALUE); + when(regionStub.containsKey(TEST_KEY)).thenReturn(true); + when(regionStub.containsKey(MISSING_KEY)).thenReturn(false); + + cacheStub = mock(Cache.class); + when(cacheStub.getRegion(TEST_REGION)).thenReturn(regionStub); + when(cacheStub.getRegion(MISSING_REGION)).thenReturn(null); + operationHandler = new RemoveRequestOperationHandler(); + stringDecoder = new StringCodec(); + } + + @Test + public void processValidKeyRemovesTheEntryAndReturnSuccess() + throws CodecAlreadyRegisteredForTypeException, UnsupportedEncodingTypeException, + CodecNotRegisteredForTypeException { + ClientProtocol.Request removeRequest = generateTestRequest(false, false); + ClientProtocol.Response response = + operationHandler.process(serializationServiceStub, removeRequest, cacheStub); + + Assert.assertEquals(ClientProtocol.Response.ResponseAPICase.REMOVERESPONSE, + response.getResponseAPICase()); + RegionAPI.RemoveResponse removeResponse = response.getRemoveResponse(); + verify(regionStub).remove(TEST_KEY); + } + + @Test + public void processReturnsUnsucessfulResponseForInvalidRegion() + throws CodecAlreadyRegisteredForTypeException, UnsupportedEncodingTypeException, + CodecNotRegisteredForTypeException { + ClientProtocol.Request removeRequest = generateTestRequest(true, false); + ClientProtocol.Response response = + operationHandler.process(serializationServiceStub, removeRequest, cacheStub); + + Assert.assertEquals(ClientProtocol.Response.ResponseAPICase.ERRORRESPONSE, + response.getResponseAPICase()); + } + + @Test + public void processReturnsKeyNotFoundWhenKeyIsNotFound() + throws CodecAlreadyRegisteredForTypeException, UnsupportedEncodingTypeException, + CodecNotRegisteredForTypeException { + ClientProtocol.Request removeRequest = generateTestRequest(false, true); + ClientProtocol.Response response = + operationHandler.process(serializationServiceStub, removeRequest, cacheStub); + + Assert.assertEquals(ClientProtocol.Response.ResponseAPICase.REMOVERESPONSE, + response.getResponseAPICase()); + RegionAPI.RemoveResponse removeResponse = response.getRemoveResponse(); + } + + @Test + public void processReturnsErrorWhenUnableToDecodeRequest() + throws CodecAlreadyRegisteredForTypeException, UnsupportedEncodingTypeException, + CodecNotRegisteredForTypeException { + CodecNotRegisteredForTypeException exception = + new CodecNotRegisteredForTypeException("error finding codec for type"); + when(serializationServiceStub.decode(BasicTypes.EncodingType.STRING, + TEST_KEY.getBytes(Charset.forName("UTF-8")))).thenThrow(exception); + + ClientProtocol.Request removeRequest = generateTestRequest(false, false); + ClientProtocol.Response response = + operationHandler.process(serializationServiceStub, removeRequest, cacheStub); + + Assert.assertEquals(ClientProtocol.Response.ResponseAPICase.ERRORRESPONSE, + response.getResponseAPICase()); + } + + private ClientProtocol.Request generateTestRequest(boolean missingRegion, boolean missingKey) + throws UnsupportedEncodingTypeException, CodecNotRegisteredForTypeException { + String region = missingRegion ? MISSING_REGION : TEST_REGION; + String key = missingKey ? MISSING_KEY : TEST_KEY; + BasicTypes.EncodedValue testKey = + ProtobufUtilities.createEncodedValue(serializationServiceStub, key); + return ProtobufRequestUtilities.createRemoveRequest(region, testKey); + } +}