StatefulService implements Service { protected <T extends ServiceDocument> T getBody(Operation op) { @SuppressWarnings("unchecked") T body = (T) op.getBody(this.context.stateType); return body; } StatefulService(Class<? extends ServiceDocument> stateType); @Override OperationProcessingChain getOperationProcessingChain(); @Override Service.ProcessingStage getProcessingStage(); @Override void handleCreate(Operation post); @Override void handleStart(Operation post); @Override void authorizeRequest(Operation op); @Override boolean queueRequest(Operation op); @Override void handleRequest(Operation request); @Override void handleRequest(Operation request, OperationProcessingStage opProcessingStage); @RouteDocumentation(supportLevel = SupportLevel.NOT_SUPPORTED) void handlePost(Operation post); @RouteDocumentation(description = "Delete this service instance") void handleDelete(Operation delete); @Override void handleStop(Operation delete); @RouteDocumentation(supportLevel = SupportLevel.NOT_SUPPORTED) void handlePatch(Operation patch); void handleOptions(Operation options); @RouteDocumentation(description = "Replace current state with the body of the request") void handlePut(Operation put); @RouteDocumentation(description = "Read the service document") void handleGet(Operation get); @Override Operation dequeueRequest(); @Override void setStat(String name, double newValue); @Override void setStat(ServiceStat s, double newValue); @Override void adjustStat(ServiceStat s, double delta); @Override void adjustStat(String name, double delta); @Override ServiceStat getStat(String name); @Override String getSelfLink(); @Override URI getUri(); @Override ServiceHost getHost(); @Override boolean hasOption(Service.ServiceOption cap); @Override void toggleOption(ServiceOption option, boolean enable); @Override void setSelfLink(String path); @Override void setHost(ServiceHost serviceHost); @Override void setOperationProcessingChain(OperationProcessingChain opProcessingChain); @Override void setProcessingStage(ProcessingStage stage); @Override Service getUtilityService(String uriPath); @Override void sendRequest(Operation op); ServiceDocumentDescription getStateDescription(); @Override ServiceDocument getDocumentTemplate(); void logSevere(Throwable e); void logSevere(String fmt, Object... args); void logSevere(Supplier<String> messageSupplier); void logInfo(String fmt, Object... args); void logInfo(Supplier<String> messageSupplier); void logFine(String fmt, Object... args); void logFine(Supplier<String> messageSupplier); void logWarning(String fmt, Object... args); void logWarning(Supplier<String> messageSupplier); void log(Level level, String fmt, Object... args); void log(Level level, Supplier<String> messageSupplier); @Override void handleMaintenance(Operation post); void handlePeriodicMaintenance(Operation post); void handleNodeGroupMaintenance(Operation post); @Override ServiceDocument setInitialState(Object state, Long version); @Override String getPeerNodeSelectorPath(); @Override void setPeerNodeSelectorPath(String link); @Override String getDocumentIndexPath(); @Override void setDocumentIndexPath(String link); @Override EnumSet<ServiceOption> getOptions(); @Override void setState(Operation op, ServiceDocument newState); @SuppressWarnings("unchecked") @Override T getState(Operation op); void setAvailable(boolean isAvailable); boolean isAvailable(); @Override void setMaintenanceIntervalMicros(long micros); @Override void setCacheClearDelayMicros(long micros); @Override long getMaintenanceIntervalMicros(); @Override long getCacheClearDelayMicros(); @Override Class<? extends ServiceDocument> getStateType(); @Override void handleConfigurationRequest(Operation request); @Override final void setAuthorizationContext(Operation op, AuthorizationContext ctx); final Signer getTokenSigner(); @Override final AuthorizationContext getSystemAuthorizationContext(); final AuthorizationContext getAuthorizationContextForSubject(String subject);  }
@Test public void skipPersistDocument() throws Throwable { this.host.startFactory(new NoUpdateOnPutService()); this.host.waitForServiceAvailable(NoUpdateOnPutService.FACTORY_LINK); String servicePath = UriUtils.buildUriPath(NoUpdateOnPutService.FACTORY_LINK, "foo"); NoUpdateOnPutService.State initialBody = new NoUpdateOnPutService.State(); initialBody.name = "init"; initialBody.documentSelfLink = servicePath; Operation initialPost = Operation.createPost(this.host, NoUpdateOnPutService.FACTORY_LINK).setBody(initialBody); NoUpdateOnPutService.State postResult = this.sender.sendAndWait(initialPost, NoUpdateOnPutService.State.class); long initialVersion = postResult.documentVersion; URI subscriptionUri = UriUtils.buildSubscriptionUri(this.host, servicePath); Operation createSubscriptionOp = Operation.createPost(subscriptionUri).setReferer(this.host.getPublicUri()); AtomicBoolean isSubscriptionCalled = new AtomicBoolean(false); this.host.startSubscriptionService(createSubscriptionOp, subscriptionOp -> { isSubscriptionCalled.set(true); subscriptionOp.complete(); }); for (int i = 0; i < 100; i++) { String newName = "modified-" + i; NoUpdateOnPutService.State putBody = new NoUpdateOnPutService.State(); putBody.name = newName; Operation put = Operation.createPut(this.host, servicePath).setBody(putBody); Operation putResponseOp = this.sender.sendAndWait(put); assertEquals(Operation.STATUS_CODE_OK, putResponseOp.getStatusCode()); assertTrue(putResponseOp.hasBody()); NoUpdateOnPutService.State putResponseBody = putResponseOp.getBody(NoUpdateOnPutService.State.class); assertEquals("response of put should be the one specified in request", newName, putResponseBody.name); NoUpdateOnPutService.State getBody = this.sender.sendAndWait(Operation.createGet(this.host, servicePath), NoUpdateOnPutService.State.class); assertEquals("document should not be updated", "init", getBody.name); assertEquals("document should not be updated", initialVersion, getBody.documentVersion); assertFalse("subscription should not be called for non modification", isSubscriptionCalled.get()); } }