|
| 1 | +package io.temporal.workflow.shared; |
| 2 | + |
| 3 | +import io.nexusrpc.OperationException; |
| 4 | +import io.nexusrpc.handler.OperationCancelDetails; |
| 5 | +import io.nexusrpc.handler.OperationContext; |
| 6 | +import io.nexusrpc.handler.OperationHandler; |
| 7 | +import io.nexusrpc.handler.OperationImpl; |
| 8 | +import io.nexusrpc.handler.OperationStartDetails; |
| 9 | +import io.nexusrpc.handler.OperationStartResult; |
| 10 | +import io.nexusrpc.handler.ServiceImpl; |
| 11 | +import java.util.UUID; |
| 12 | +import java.util.concurrent.atomic.AtomicInteger; |
| 13 | + |
| 14 | +/** |
| 15 | + * Shared {@link TestNexusServices.TestNexusService1} implementation used by the standalone Nexus |
| 16 | + * client tests. Behaviour is driven entirely by the input string: |
| 17 | + * |
| 18 | + * <ul> |
| 19 | + * <li>An input starting with {@link #FAIL_PREFIX} causes {@code start} to throw an {@link |
| 20 | + * OperationException#failed} so callers see a non-retryable handler failure. |
| 21 | + * <li>An input starting with {@link #ASYNC_PREFIX} causes {@code start} to return an |
| 22 | + * async-started result with a synthetic operation token; the operation stays in {@code |
| 23 | + * RUNNING} until something terminal (cancel that takes effect, terminate, schedule-to-close) |
| 24 | + * transitions it. |
| 25 | + * <li>Any other input is echoed back as {@code "echo:" + input}. |
| 26 | + * </ul> |
| 27 | + * |
| 28 | + * <p>Cancel callbacks increment {@link #cancelInvocations} so tests can assert cancel-RPC delivery |
| 29 | + * end-to-end. The counter is process-wide; tests that care should capture a baseline before the |
| 30 | + * cancel and assert the post-cancel value is strictly greater. |
| 31 | + */ |
| 32 | +@ServiceImpl(service = TestNexusServices.TestNexusService1.class) |
| 33 | +public class EchoNexusServiceImpl { |
| 34 | + |
| 35 | + /** Inputs starting with this prefix make {@code start} throw, exercising the failure path. */ |
| 36 | + public static final String FAIL_PREFIX = "FAIL:"; |
| 37 | + |
| 38 | + /** |
| 39 | + * Inputs starting with this prefix make {@code start} return an async-started result without ever |
| 40 | + * completing the operation. Used by cancel/terminate tests so the operation stays in {@code |
| 41 | + * RUNNING} long enough for the lifecycle RPC to be observed. |
| 42 | + */ |
| 43 | + public static final String ASYNC_PREFIX = "ASYNC:"; |
| 44 | + |
| 45 | + /** |
| 46 | + * Incremented every time the worker invokes the handler's {@code cancel(...)} callback. Tests |
| 47 | + * that want to assert end-to-end cancel-RPC delivery (client → server → worker) read the value |
| 48 | + * before the cancel, issue the cancel, then poll until this counter exceeds the baseline. |
| 49 | + */ |
| 50 | + public static final AtomicInteger cancelInvocations = new AtomicInteger(); |
| 51 | + |
| 52 | + @OperationImpl |
| 53 | + public OperationHandler<String, String> operation() { |
| 54 | + return new OperationHandler<String, String>() { |
| 55 | + @Override |
| 56 | + public OperationStartResult<String> start( |
| 57 | + OperationContext context, OperationStartDetails details, String input) |
| 58 | + throws OperationException { |
| 59 | + if (input != null && input.startsWith(FAIL_PREFIX)) { |
| 60 | + throw OperationException.failed("intentional failure: " + input); |
| 61 | + } |
| 62 | + if (input != null && input.startsWith(ASYNC_PREFIX)) { |
| 63 | + return OperationStartResult.async("token-" + UUID.randomUUID()); |
| 64 | + } |
| 65 | + return OperationStartResult.sync("echo:" + (input == null ? "<null>" : input)); |
| 66 | + } |
| 67 | + |
| 68 | + @Override |
| 69 | + public void cancel(OperationContext context, OperationCancelDetails details) { |
| 70 | + cancelInvocations.incrementAndGet(); |
| 71 | + } |
| 72 | + }; |
| 73 | + } |
| 74 | +} |
0 commit comments