• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

temporalio / sdk-java / #349

03 Aug 2026 05:42AM UTC coverage: 68.221% (+0.08%) from 68.143%
#349

push

github

web-flow
Add Standalone Activities to Temporal Nexus Operation Handler (#2918)

* Enable Nexus activity operations without regressing workflow updates

Compose standalone activity support with the workflow-update Nexus model already present on master. Shared token, callback, link, client, and cancellation paths retain both operation families.

Constraint: Preserve the workflow-update token and API contracts from master
Rejected: Choose one conflict side | each side would drop a supported Nexus operation family
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep activity execution at token type 2 and workflow update at token type 3
Tested: Focused temporal-sdk token, link, invoker, client, async activity, and cancellation tests
Not-tested: Full repository suite and real-server-only standalone activity cases

* Make sure we test attaching

* Make sure to handle ActivityAlreadyStartedException

* Add test with SANO

* Bump test server

7220 of 12610 branches covered (57.26%)

Branch coverage included in aggregate %.

182 of 283 new or added lines in 15 files covered. (64.31%)

13 existing lines in 3 files now uncovered.

29918 of 41828 relevant lines covered (71.53%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

29.55
/temporal-sdk/src/main/java/io/temporal/client/ActivityClientImpl.java
1
package io.temporal.client;
2

3
import com.uber.m3.tally.Scope;
4
import io.temporal.api.common.v1.Payload;
5
import io.temporal.common.context.ContextPropagator;
6
import io.temporal.common.interceptors.ActivityClientCallsInterceptor;
7
import io.temporal.common.interceptors.ActivityClientInterceptor;
8
import io.temporal.common.interceptors.Header;
9
import io.temporal.internal.client.ActivityClientInternal;
10
import io.temporal.internal.client.ActivityHandleImpl;
11
import io.temporal.internal.client.RootActivityClientInvoker;
12
import io.temporal.internal.client.external.GenericWorkflowClientImpl;
13
import io.temporal.internal.client.external.ManualActivityCompletionClientFactory;
14
import io.temporal.internal.util.MethodExtractor;
15
import io.temporal.serviceclient.MetricsTag;
16
import io.temporal.serviceclient.WorkflowServiceStubs;
17
import io.temporal.workflow.Functions;
18
import java.lang.reflect.Method;
19
import java.lang.reflect.Type;
20
import java.util.Arrays;
21
import java.util.HashMap;
22
import java.util.List;
23
import java.util.Map;
24
import java.util.stream.Stream;
25
import javax.annotation.Nullable;
26

27
/**
28
 * Implementation of {@link ActivityClient} that delegates calls through the activity interceptor
29
 * chain and ultimately to the Temporal service.
30
 */
31
class ActivityClientImpl implements ActivityClient, ActivityClientInternal {
32

33
  private final WorkflowServiceStubs stubs;
34
  private final ActivityClientOptions options;
35
  private final ActivityClientCallsInterceptor invoker;
36
  private final ManualActivityCompletionClientFactory manualActivityCompletionClientFactory;
37
  private final Scope metricsScope;
38

39
  ActivityClientImpl(WorkflowServiceStubs stubs, ActivityClientOptions options) {
1✔
40
    this.stubs = stubs;
1✔
41
    this.options = options;
1✔
42
    this.metricsScope =
1✔
43
        stubs.getOptions().getMetricsScope().tagged(MetricsTag.defaultTags(options.getNamespace()));
1✔
44
    GenericWorkflowClientImpl genericClient = new GenericWorkflowClientImpl(stubs, metricsScope);
1✔
45
    this.invoker = initializeClientInvoker(genericClient, options);
1✔
46
    this.manualActivityCompletionClientFactory =
1✔
47
        ManualActivityCompletionClientFactory.newFactory(
1✔
48
            stubs, options.getNamespace(), options.getIdentity(), options.getDataConverter());
1✔
49
  }
1✔
50

51
  private static ActivityClientCallsInterceptor initializeClientInvoker(
52
      GenericWorkflowClientImpl genericClient, ActivityClientOptions options) {
53
    ActivityClientCallsInterceptor invoker = new RootActivityClientInvoker(genericClient, options);
1✔
54
    for (ActivityClientInterceptor interceptor : options.getInterceptors()) {
1✔
55
      invoker = interceptor.activityClientCallsInterceptor(invoker);
1✔
56
    }
1✔
57
    return invoker;
1✔
58
  }
59

60
  @Override
61
  public ActivityClientCallsInterceptor getInvoker() {
NEW
62
    return invoker;
×
63
  }
64

65
  // ---- Interface-based start (Proc variants) ----
66

67
  @Override
68
  public <I> ActivityHandle<Void> start(
69
      Class<I> activityInterface, Functions.Proc1<I> activity, StartActivityOptions options) {
70
    String activityType =
1✔
71
        MethodExtractor.activityTypeName(
1✔
72
            activityInterface, MethodExtractor.extract(activityInterface, activity));
1✔
73
    UntypedActivityHandle untyped = start(activityType, options, new Object[0]);
1✔
74
    return ActivityHandle.fromUntyped(untyped, Void.class, null);
1✔
75
  }
76

77
  @Override
78
  public <I, A1> ActivityHandle<Void> start(
79
      Class<I> activityInterface,
80
      Functions.Proc2<I, A1> activity,
81
      StartActivityOptions options,
82
      A1 arg1) {
83
    String activityType =
×
84
        MethodExtractor.activityTypeName(
×
85
            activityInterface, MethodExtractor.extract(activityInterface, activity));
×
86
    UntypedActivityHandle untyped = start(activityType, options, arg1);
×
87
    return ActivityHandle.fromUntyped(untyped, Void.class, null);
×
88
  }
89

90
  @Override
91
  public <I, A1, A2> ActivityHandle<Void> start(
92
      Class<I> activityInterface,
93
      Functions.Proc3<I, A1, A2> activity,
94
      StartActivityOptions options,
95
      A1 arg1,
96
      A2 arg2) {
97
    String activityType =
×
98
        MethodExtractor.activityTypeName(
×
99
            activityInterface, MethodExtractor.extract(activityInterface, activity));
×
100
    UntypedActivityHandle untyped = start(activityType, options, arg1, arg2);
×
101
    return ActivityHandle.fromUntyped(untyped, Void.class, null);
×
102
  }
103

104
  @Override
105
  public <I, A1, A2, A3> ActivityHandle<Void> start(
106
      Class<I> activityInterface,
107
      Functions.Proc4<I, A1, A2, A3> activity,
108
      StartActivityOptions options,
109
      A1 arg1,
110
      A2 arg2,
111
      A3 arg3) {
112
    String activityType =
×
113
        MethodExtractor.activityTypeName(
×
114
            activityInterface, MethodExtractor.extract(activityInterface, activity));
×
115
    UntypedActivityHandle untyped = start(activityType, options, arg1, arg2, arg3);
×
116
    return ActivityHandle.fromUntyped(untyped, Void.class, null);
×
117
  }
118

119
  @Override
120
  public <I, A1, A2, A3, A4> ActivityHandle<Void> start(
121
      Class<I> activityInterface,
122
      Functions.Proc5<I, A1, A2, A3, A4> activity,
123
      StartActivityOptions options,
124
      A1 arg1,
125
      A2 arg2,
126
      A3 arg3,
127
      A4 arg4) {
128
    String activityType =
×
129
        MethodExtractor.activityTypeName(
×
130
            activityInterface, MethodExtractor.extract(activityInterface, activity));
×
131
    UntypedActivityHandle untyped = start(activityType, options, arg1, arg2, arg3, arg4);
×
132
    return ActivityHandle.fromUntyped(untyped, Void.class, null);
×
133
  }
134

135
  @Override
136
  public <I, A1, A2, A3, A4, A5> ActivityHandle<Void> start(
137
      Class<I> activityInterface,
138
      Functions.Proc6<I, A1, A2, A3, A4, A5> activity,
139
      StartActivityOptions options,
140
      A1 arg1,
141
      A2 arg2,
142
      A3 arg3,
143
      A4 arg4,
144
      A5 arg5) {
145
    String activityType =
×
146
        MethodExtractor.activityTypeName(
×
147
            activityInterface, MethodExtractor.extract(activityInterface, activity));
×
148
    UntypedActivityHandle untyped = start(activityType, options, arg1, arg2, arg3, arg4, arg5);
×
149
    return ActivityHandle.fromUntyped(untyped, Void.class, null);
×
150
  }
151

152
  @Override
153
  public <I, A1, A2, A3, A4, A5, A6> ActivityHandle<Void> start(
154
      Class<I> activityInterface,
155
      Functions.Proc7<I, A1, A2, A3, A4, A5, A6> activity,
156
      StartActivityOptions options,
157
      A1 arg1,
158
      A2 arg2,
159
      A3 arg3,
160
      A4 arg4,
161
      A5 arg5,
162
      A6 arg6) {
163
    String activityType =
×
164
        MethodExtractor.activityTypeName(
×
165
            activityInterface, MethodExtractor.extract(activityInterface, activity));
×
166
    UntypedActivityHandle untyped =
×
167
        start(activityType, options, arg1, arg2, arg3, arg4, arg5, arg6);
×
168
    return ActivityHandle.fromUntyped(untyped, Void.class, null);
×
169
  }
170

171
  // ---- Interface-based start (Func variants) ----
172

173
  @Override
174
  public <I, R> ActivityHandle<R> start(
175
      Class<I> activityInterface, Functions.Func1<I, R> activity, StartActivityOptions options) {
176
    Method method = MethodExtractor.extract(activityInterface, activity);
×
177
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
178
    @SuppressWarnings("unchecked")
179
    Class<R> resultClass = (Class<R>) method.getReturnType();
×
180
    Type resultType = method.getGenericReturnType();
×
181
    UntypedActivityHandle untyped = start(activityType, options, new Object[0]);
×
182
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
183
  }
184

185
  @Override
186
  public <I, A1, R> ActivityHandle<R> start(
187
      Class<I> activityInterface,
188
      Functions.Func2<I, A1, R> activity,
189
      StartActivityOptions options,
190
      A1 arg1) {
191
    Method method = MethodExtractor.extract(activityInterface, activity);
×
192
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
193
    @SuppressWarnings("unchecked")
194
    Class<R> resultClass = (Class<R>) method.getReturnType();
×
195
    Type resultType = method.getGenericReturnType();
×
196
    UntypedActivityHandle untyped = start(activityType, options, arg1);
×
197
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
198
  }
199

200
  @Override
201
  public <I, A1, A2, R> ActivityHandle<R> start(
202
      Class<I> activityInterface,
203
      Functions.Func3<I, A1, A2, R> activity,
204
      StartActivityOptions options,
205
      A1 arg1,
206
      A2 arg2) {
207
    Method method = MethodExtractor.extract(activityInterface, activity);
×
208
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
209
    @SuppressWarnings("unchecked")
210
    Class<R> resultClass = (Class<R>) method.getReturnType();
×
211
    Type resultType = method.getGenericReturnType();
×
212
    UntypedActivityHandle untyped = start(activityType, options, arg1, arg2);
×
213
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
214
  }
215

216
  @Override
217
  public <I, A1, A2, A3, R> ActivityHandle<R> start(
218
      Class<I> activityInterface,
219
      Functions.Func4<I, A1, A2, A3, R> activity,
220
      StartActivityOptions options,
221
      A1 arg1,
222
      A2 arg2,
223
      A3 arg3) {
224
    Method method = MethodExtractor.extract(activityInterface, activity);
×
225
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
226
    @SuppressWarnings("unchecked")
227
    Class<R> resultClass = (Class<R>) method.getReturnType();
×
228
    Type resultType = method.getGenericReturnType();
×
229
    UntypedActivityHandle untyped = start(activityType, options, arg1, arg2, arg3);
×
230
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
231
  }
232

233
  @Override
234
  public <I, A1, A2, A3, A4, R> ActivityHandle<R> start(
235
      Class<I> activityInterface,
236
      Functions.Func5<I, A1, A2, A3, A4, R> activity,
237
      StartActivityOptions options,
238
      A1 arg1,
239
      A2 arg2,
240
      A3 arg3,
241
      A4 arg4) {
242
    Method method = MethodExtractor.extract(activityInterface, activity);
×
243
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
244
    @SuppressWarnings("unchecked")
245
    Class<R> resultClass = (Class<R>) method.getReturnType();
×
246
    Type resultType = method.getGenericReturnType();
×
247
    UntypedActivityHandle untyped = start(activityType, options, arg1, arg2, arg3, arg4);
×
248
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
249
  }
250

251
  @Override
252
  public <I, A1, A2, A3, A4, A5, R> ActivityHandle<R> start(
253
      Class<I> activityInterface,
254
      Functions.Func6<I, A1, A2, A3, A4, A5, R> activity,
255
      StartActivityOptions options,
256
      A1 arg1,
257
      A2 arg2,
258
      A3 arg3,
259
      A4 arg4,
260
      A5 arg5) {
261
    Method method = MethodExtractor.extract(activityInterface, activity);
×
262
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
263
    @SuppressWarnings("unchecked")
264
    Class<R> resultClass = (Class<R>) method.getReturnType();
×
265
    Type resultType = method.getGenericReturnType();
×
266
    UntypedActivityHandle untyped = start(activityType, options, arg1, arg2, arg3, arg4, arg5);
×
267
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
268
  }
269

270
  @Override
271
  public <I, A1, A2, A3, A4, A5, A6, R> ActivityHandle<R> start(
272
      Class<I> activityInterface,
273
      Functions.Func7<I, A1, A2, A3, A4, A5, A6, R> activity,
274
      StartActivityOptions options,
275
      A1 arg1,
276
      A2 arg2,
277
      A3 arg3,
278
      A4 arg4,
279
      A5 arg5,
280
      A6 arg6) {
281
    Method method = MethodExtractor.extract(activityInterface, activity);
×
282
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
283
    @SuppressWarnings("unchecked")
284
    Class<R> resultClass = (Class<R>) method.getReturnType();
×
285
    Type resultType = method.getGenericReturnType();
×
286
    UntypedActivityHandle untyped =
×
287
        start(activityType, options, arg1, arg2, arg3, arg4, arg5, arg6);
×
288
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
289
  }
290

291
  // ---- String-based start ----
292

293
  @Override
294
  public UntypedActivityHandle start(
295
      String activityType, StartActivityOptions options, @Nullable Object... args) {
296
    ActivityClientCallsInterceptor.StartActivityOutput output =
1✔
297
        invoker.startActivity(
1✔
298
            new ActivityClientCallsInterceptor.StartActivityInput(
299
                activityType,
300
                Arrays.asList(args != null ? args : new Object[0]),
1!
301
                options,
302
                propagatedHeader()));
1✔
303
    return new ActivityHandleImpl(output.getActivityId(), output.getActivityRunId(), invoker);
1✔
304
  }
305

306
  @Override
307
  public <R> ActivityHandle<R> start(
308
      String activityType,
309
      Class<R> resultClass,
310
      StartActivityOptions options,
311
      @Nullable Object... args) {
312
    return start(activityType, resultClass, null, options, args);
×
313
  }
314

315
  @Override
316
  public <R> ActivityHandle<R> start(
317
      String activityType,
318
      Class<R> resultClass,
319
      Type resultType,
320
      StartActivityOptions options,
321
      @Nullable Object... args) {
322
    UntypedActivityHandle untyped = start(activityType, options, args);
×
323
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
324
  }
325

326
  // ---- Handle lookup ----
327

328
  @Override
329
  public UntypedActivityHandle getHandle(String activityId, @Nullable String activityRunId) {
330
    return new ActivityHandleImpl(activityId, activityRunId, invoker);
×
331
  }
332

333
  @Override
334
  public <R> ActivityHandle<R> getHandle(
335
      String activityId, @Nullable String activityRunId, Class<R> resultClass) {
336
    return getHandle(activityId, activityRunId, resultClass, null);
×
337
  }
338

339
  @Override
340
  public <R> ActivityHandle<R> getHandle(
341
      String activityId,
342
      @Nullable String activityRunId,
343
      Class<R> resultClass,
344
      @Nullable Type resultType) {
345
    UntypedActivityHandle untyped = getHandle(activityId, activityRunId);
×
346
    return ActivityHandle.fromUntyped(untyped, resultClass, resultType);
×
347
  }
348

349
  private Header propagatedHeader() {
350
    List<ContextPropagator> propagators = options.getContextPropagators();
1✔
351
    if (propagators.isEmpty()) {
1!
352
      return Header.empty();
×
353
    }
354
    Map<String, Payload> result = new HashMap<>();
1✔
355
    for (ContextPropagator propagator : propagators) {
1✔
356
      result.putAll(propagator.serializeContext(propagator.getCurrentContext()));
1✔
357
    }
1✔
358
    return new Header(result);
1✔
359
  }
360

361
  // ---- List / count ----
362

363
  @Override
364
  public Stream<ActivityExecutionMetadata> listExecutions(String query) {
365
    return invoker
×
366
        .listActivities(new ActivityClientCallsInterceptor.ListActivitiesInput(query))
×
367
        .getStream();
×
368
  }
369

370
  @Override
371
  public ActivityExecutionCount countExecutions(String query) {
372
    return invoker
×
373
        .countActivities(new ActivityClientCallsInterceptor.CountActivitiesInput(query))
×
374
        .getCount();
×
375
  }
376

377
  // ---- Completion client ----
378

379
  @Override
380
  public ActivityCompletionClient newActivityCompletionClient() {
381
    return new ActivityCompletionClientImpl(
×
382
        manualActivityCompletionClientFactory, () -> {}, metricsScope, null);
×
383
  }
384
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc