KeyResource { private KeyValue compareAndSet(SetValue setValue) throws Exception { LOGGER.info("cas: {}->{} if {}", key, setValue.getNewValue(), setValue.getExpectedValue()); try { ListenableFuture<KeyValue> casFuture = distributedStore.compareAndSet(key, setValue); return casFuture.get(ResourceConstants.COMMAND_TIMEOUT, ResourceConstants.COMMAND_TIMEOUT_TIME_UNIT); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause instanceof NotLeaderException) { throw createCannotSubmitCommandException((NotLeaderException) cause); } else if (cause instanceof KayVeeException) { throw (KayVeeException) cause; } else { throw e; } } } KeyResource(String key, Set<ClusterMember> members, DistributedStore distributedStore); @GET @Produces(MediaType.APPLICATION_JSON) KeyValue get(); @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Nullable KeyValue update(SetValue setValue); @DELETE void delete();  }
@Test public void shouldReturnNoContentIfCASSucceedsAndDeletesAKey() { SetValue deleteKeySetValue = new SetValue(); deleteKeySetValue.setExpectedValue(EXPECTED_VALUE); deleteKeySetValue.setNewValue(null); when(distributedStore.compareAndSet(KEY, deleteKeySetValue)).thenReturn(Futures.<KeyValue>immediateFuture(null)); ClientResponse response = client().resource(REQUEST_URI).type(MediaType.APPLICATION_JSON_TYPE).put(ClientResponse.class, deleteKeySetValue); assertThat(response.getClientResponseStatus(), equalTo(ClientResponse.Status.NO_CONTENT)); }
@Test public void shouldReturnConflictIfCompareAndSetFailsBecauseAValueMismatchExceptionIsThrown() { when(distributedStore.compareAndSet(KEY, casValue)).thenReturn( Futures.<KeyValue>immediateFailedFuture(new ValueMismatchException(KEY, EXPECTED_VALUE, "EXISTING_VALUE"))); assertThatResourceMethodReturnsStatus( client().resource(REQUEST_URI).type(MediaType.APPLICATION_JSON_TYPE).entity(casValue), "PUT", ClientResponse.Status.CONFLICT ); }
@Test public void shouldReturnConflictIfCompareAndSetFailsBecauseAKeyAlreadyExistsExceptionIsThrown() { final SetValue newKeyValue = new SetValue(); newKeyValue.setExpectedValue(null); newKeyValue.setNewValue(NEW_VALUE); when(distributedStore.compareAndSet(KEY, newKeyValue)).thenReturn(Futures.<KeyValue>immediateFailedFuture(new KeyAlreadyExistsException(KEY))); assertThatResourceMethodReturnsStatus( client().resource(REQUEST_URI).type(MediaType.APPLICATION_JSON_TYPE).entity(newKeyValue), "PUT", ClientResponse.Status.CONFLICT ); }
@Test public void shouldReturnNotFoundIfCompareAndSetFailsBecauseAKeyNotFoundExceptionIsThrown() { when(distributedStore.compareAndSet(KEY, casValue)).thenReturn(Futures.<KeyValue>immediateFailedFuture(new KeyNotFoundException(KEY))); assertThatResourceMethodReturnsStatus( client().resource(REQUEST_URI).type(MediaType.APPLICATION_JSON_TYPE).entity(casValue), "PUT", ClientResponse.Status.NOT_FOUND ); }
@Test public void shouldReturnInternalServerErrorIfCompareAndSetFailsBecauseADatabaseExceptionIsThrownInsideDistributedStore() { when(distributedStore.compareAndSet(KEY, casValue)).thenReturn(Futures.<KeyValue>immediateFailedFuture(new StorageException(new SQLException("DATABASE GONE")))); assertThatValidCompareAndSetRequestReturnsErrorStatus(ClientResponse.Status.INTERNAL_SERVER_ERROR); }
@Test public void shouldReturnInternalServerErrorIfCompareAndSetFailsBecauseAnExceptionIsThrownInServiceCode() { when(distributedStore.compareAndSet(KEY, casValue)).thenThrow(new IllegalStateException("service code terminated unexpectedly")); assertThatValidCompareAndSetRequestReturnsErrorStatus(ClientResponse.Status.INTERNAL_SERVER_ERROR); }
@Test public void shouldReturnServiceUnavailableWithNoLocationIfCompareAndSetFailsBecauseNotLeaderExceptionIsThrownWithTheLeaderSetToNull() { when(distributedStore.compareAndSet(KEY, casValue)).thenReturn(Futures.<KeyValue>immediateFailedFuture(new NotLeaderException(self, null))); assertThatValidCompareAndSetRequestReturnsErrorStatus(ClientResponse.Status.SERVICE_UNAVAILABLE); }
@Test public void shouldReturnRedirectWithLocationIfCompareAndSetFailsBecauseNotLeaderExceptionIsThrownWithTheLeaderSetToAValidLeader() { when(distributedStore.compareAndSet(KEY, casValue)).thenReturn(Futures.<KeyValue>immediateFailedFuture(new NotLeaderException(self, leader))); assertThatValidCompareAndSetRequestReturnsErrorStatus(ClientResponse.Status.MOVED_PERMANENTLY); }
@Test public void shouldReturnInternalServerErrorWithNoLocationIfCompareAndSetFailsBecauseNotLeaderExceptionIsThrownWithTheLeaderSetToAnInvalidLeader() { when(distributedStore.compareAndSet(KEY, casValue)).thenReturn(Futures.<KeyValue>immediateFailedFuture(new NotLeaderException(self, "UNKNOWN_LEADER"))); assertThatValidCompareAndSetRequestReturnsErrorStatus(ClientResponse.Status.INTERNAL_SERVER_ERROR); }