Skip to content

Commit c6e0306

Browse files
Special behavior for Temporal built-in prefixes (#2409)
1 parent 00f7dd0 commit c6e0306

17 files changed

+562
-1
lines changed

temporal-sdk/src/main/java/io/temporal/activity/ActivityMethod.java

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
*
4141
* <p>Be careful with names that contain special characters, as these names can be used as metric
4242
* tags. Systems like Prometheus ignore metrics which have tags with unsupported characters.
43+
*
44+
* <p>Name cannot start with __temporal_ as it is reserved for internal use.
4345
*/
4446
String name() default "";
4547
}

temporal-sdk/src/main/java/io/temporal/common/metadata/POJOActivityImplMetadata.java

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.common.collect.ImmutableList;
2424
import io.temporal.activity.ActivityMethod;
2525
import io.temporal.common.MethodRetry;
26+
import io.temporal.internal.common.InternalUtils;
2627
import java.lang.reflect.Method;
2728
import java.util.ArrayList;
2829
import java.util.HashMap;
@@ -90,6 +91,7 @@ private POJOActivityImplMetadata(Class<?> implClass) {
9091
activityInterfaces.add(interfaceMetadata);
9192
List<POJOActivityMethodMetadata> methods = interfaceMetadata.getMethodsMetadata();
9293
for (POJOActivityMethodMetadata methodMetadata : methods) {
94+
InternalUtils.checkMethodName(methodMetadata);
9395
POJOActivityMethodMetadata registeredMM =
9496
byName.put(methodMetadata.getActivityTypeName(), methodMetadata);
9597
if (registeredMM != null && !registeredMM.equals(methodMetadata)) {

temporal-sdk/src/main/java/io/temporal/common/metadata/POJOWorkflowImplMetadata.java

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.google.common.collect.ImmutableList;
2424
import io.temporal.common.Experimental;
25+
import io.temporal.internal.common.InternalUtils;
2526
import io.temporal.internal.common.env.ReflectionUtils;
2627
import java.lang.reflect.Constructor;
2728
import java.util.*;
@@ -145,6 +146,8 @@ private POJOWorkflowImplMetadata(
145146
+ methodMetadata.getWorkflowMethod()
146147
+ "\"");
147148
}
149+
// Validate the method name is not using any reserved prefixes or names.
150+
InternalUtils.checkMethodName(methodMetadata);
148151
switch (methodMetadata.getType()) {
149152
case WORKFLOW:
150153
workflowMethods.put(methodMetadata.getName(), methodMetadata);

temporal-sdk/src/main/java/io/temporal/internal/common/InternalUtils.java

+59
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@
2222

2323
import com.google.common.base.Defaults;
2424
import io.nexusrpc.Header;
25+
import io.nexusrpc.handler.ServiceImplInstance;
2526
import io.temporal.api.common.v1.Callback;
2627
import io.temporal.api.enums.v1.TaskQueueKind;
2728
import io.temporal.api.taskqueue.v1.TaskQueue;
2829
import io.temporal.client.WorkflowOptions;
2930
import io.temporal.client.WorkflowStub;
31+
import io.temporal.common.metadata.POJOActivityMethodMetadata;
32+
import io.temporal.common.metadata.POJOWorkflowMethodMetadata;
33+
import io.temporal.common.metadata.WorkflowMethodType;
3034
import io.temporal.internal.client.NexusStartWorkflowRequest;
3135
import java.util.Arrays;
3236
import java.util.Map;
@@ -37,7 +41,11 @@
3741

3842
/** Utility functions shared by the implementation code. */
3943
public final class InternalUtils {
44+
public static String TEMPORAL_RESERVED_PREFIX = "__temporal_";
45+
4046
private static final Logger log = LoggerFactory.getLogger(InternalUtils.class);
47+
private static String QUERY_TYPE_STACK_TRACE = "__stack_trace";
48+
private static String ENHANCED_QUERY_TYPE_STACK_TRACE = "__enhanced_stack_trace";
4149

4250
public static TaskQueue createStickyTaskQueue(
4351
String stickyTaskQueueName, String normalTaskQueueName) {
@@ -135,6 +143,57 @@ public static WorkflowStub createNexusBoundStub(
135143
return stub.newInstance(nexusWorkflowOptions.build());
136144
}
137145

146+
/** Check the method name for reserved prefixes or names. */
147+
public static void checkMethodName(POJOWorkflowMethodMetadata methodMetadata) {
148+
if (methodMetadata.getName().startsWith(TEMPORAL_RESERVED_PREFIX)) {
149+
throw new IllegalArgumentException(
150+
methodMetadata.getType().toString().toLowerCase()
151+
+ " name \""
152+
+ methodMetadata.getName()
153+
+ "\" must not start with \""
154+
+ TEMPORAL_RESERVED_PREFIX
155+
+ "\"");
156+
}
157+
if (methodMetadata.getType().equals(WorkflowMethodType.QUERY)
158+
&& (methodMetadata.getName().equals(QUERY_TYPE_STACK_TRACE)
159+
|| methodMetadata.getName().equals(ENHANCED_QUERY_TYPE_STACK_TRACE))) {
160+
throw new IllegalArgumentException(
161+
"Query method name \"" + methodMetadata.getName() + "\" is reserved for internal use");
162+
}
163+
}
164+
165+
public static void checkMethodName(POJOActivityMethodMetadata methodMetadata) {
166+
if (methodMetadata.getActivityTypeName().startsWith(TEMPORAL_RESERVED_PREFIX)) {
167+
throw new IllegalArgumentException(
168+
"Activity name \""
169+
+ methodMetadata.getActivityTypeName()
170+
+ "\" must not start with \""
171+
+ TEMPORAL_RESERVED_PREFIX
172+
+ "\"");
173+
}
174+
}
175+
138176
/** Prohibit instantiation */
139177
private InternalUtils() {}
178+
179+
public static void checkMethodName(ServiceImplInstance instance) {
180+
if (instance.getDefinition().getName().startsWith(TEMPORAL_RESERVED_PREFIX)) {
181+
throw new IllegalArgumentException(
182+
"Service name \""
183+
+ instance.getDefinition().getName()
184+
+ "\" must not start with \""
185+
+ TEMPORAL_RESERVED_PREFIX
186+
+ "\"");
187+
}
188+
for (String operationName : instance.getDefinition().getOperations().keySet()) {
189+
if (operationName.startsWith(TEMPORAL_RESERVED_PREFIX)) {
190+
throw new IllegalArgumentException(
191+
"Operation name \""
192+
+ operationName
193+
+ "\" must not start with \""
194+
+ TEMPORAL_RESERVED_PREFIX
195+
+ "\"");
196+
}
197+
}
198+
}
140199
}

temporal-sdk/src/main/java/io/temporal/internal/nexus/NexusTaskHandlerImpl.java

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import io.temporal.common.converter.DataConverter;
3636
import io.temporal.common.interceptors.WorkerInterceptor;
3737
import io.temporal.failure.ApplicationFailure;
38+
import io.temporal.internal.common.InternalUtils;
3839
import io.temporal.internal.common.NexusUtil;
3940
import io.temporal.internal.worker.NexusTask;
4041
import io.temporal.internal.worker.NexusTaskHandler;
@@ -331,6 +332,7 @@ private void registerNexusService(Object nexusService) {
331332
throw new IllegalArgumentException("Nexus service object instance expected, not the class");
332333
}
333334
ServiceImplInstance instance = ServiceImplInstance.fromInstance(nexusService);
335+
InternalUtils.checkMethodName(instance);
334336
if (serviceImplInstances.put(instance.getDefinition().getName(), instance) != null) {
335337
throw new TypeAlreadyRegisteredException(
336338
instance.getDefinition().getName(),

temporal-sdk/src/main/java/io/temporal/internal/sync/QueryDispatcher.java

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
package io.temporal.internal.sync;
2222

23+
import static io.temporal.internal.common.InternalUtils.TEMPORAL_RESERVED_PREFIX;
24+
2325
import io.temporal.api.common.v1.Payloads;
2426
import io.temporal.api.sdk.v1.WorkflowInteractionDefinition;
2527
import io.temporal.common.converter.DataConverter;
@@ -72,6 +74,10 @@ public WorkflowInboundCallsInterceptor.QueryOutput handleInterceptedQuery(
7274
public Optional<Payloads> handleQuery(String queryName, Header header, Optional<Payloads> input) {
7375
WorkflowOutboundCallsInterceptor.RegisterQueryInput handler = queryCallbacks.get(queryName);
7476
Object[] args;
77+
if (queryName.startsWith(TEMPORAL_RESERVED_PREFIX)) {
78+
throw new IllegalArgumentException(
79+
"Unknown query type: " + queryName + ", knownTypes=" + queryCallbacks.keySet());
80+
}
7581
if (handler == null) {
7682
if (dynamicQueryHandler == null) {
7783
throw new IllegalArgumentException(

temporal-sdk/src/main/java/io/temporal/internal/sync/SignalDispatcher.java

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
package io.temporal.internal.sync;
2222

23+
import static io.temporal.internal.common.InternalUtils.TEMPORAL_RESERVED_PREFIX;
24+
2325
import io.temporal.api.common.v1.Payloads;
2426
import io.temporal.api.sdk.v1.WorkflowInteractionDefinition;
2527
import io.temporal.common.converter.DataConverter;
@@ -86,6 +88,10 @@ public void handleSignal(
8688
signalCallbacks.get(signalName);
8789
Object[] args;
8890
HandlerUnfinishedPolicy policy;
91+
if (signalName.startsWith(TEMPORAL_RESERVED_PREFIX)) {
92+
// Ignore internal signals
93+
return;
94+
}
8995
if (handler == null) {
9096
if (dynamicSignalHandler == null) {
9197
signalBuffer.add(new SignalData(signalName, input, eventId, header));

temporal-sdk/src/main/java/io/temporal/internal/sync/UpdateDispatcher.java

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
package io.temporal.internal.sync;
2222

23+
import static io.temporal.internal.common.InternalUtils.TEMPORAL_RESERVED_PREFIX;
24+
2325
import io.temporal.api.common.v1.Payloads;
2426
import io.temporal.api.sdk.v1.WorkflowInteractionDefinition;
2527
import io.temporal.common.converter.DataConverter;
@@ -61,6 +63,10 @@ public void handleValidateUpdate(
6163
updateCallbacks.get(updateName);
6264
Object[] args;
6365
HandlerUnfinishedPolicy policy;
66+
if (updateName.startsWith(TEMPORAL_RESERVED_PREFIX)) {
67+
throw new IllegalArgumentException(
68+
"Unknown update name: " + updateName + ", knownTypes=" + updateCallbacks.keySet());
69+
}
6470
if (handler == null) {
6571
if (dynamicUpdateHandler == null) {
6672
throw new IllegalArgumentException(

temporal-sdk/src/main/java/io/temporal/workflow/QueryMethod.java

+3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
*
4242
* <p>Be careful about names that contain special characters. These names can be used as metric
4343
* tags. And systems like prometheus ignore metrics which have tags with unsupported characters.
44+
*
45+
* <p>Name cannot start with __temporal as it is reserved for internal use. The name also cannot
46+
* be __stack_trace or __enhanced_stack_trace as they are reserved for internal use.
4447
*/
4548
String name() default "";
4649

temporal-sdk/src/main/java/io/temporal/workflow/SignalMethod.java

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
*
5858
* <p>Be careful about names that contain special characters. These names can be used as metric
5959
* tags. And systems like prometheus ignore metrics which have tags with unsupported characters.
60+
*
61+
* <p>Name cannot start with __temporal_ as it is reserved for internal use.
6062
*/
6163
String name() default "";
6264

temporal-sdk/src/main/java/io/temporal/workflow/UpdateMethod.java

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
*
4040
* <p>Be careful about names that contain special characters. These names can be used as metric
4141
* tags. And systems like prometheus ignore metrics which have tags with unsupported characters.
42+
*
43+
* <p>Name cannot start with __temporal as it is reserved for internal use.
4244
*/
4345
String name() default "";
4446

temporal-sdk/src/main/java/io/temporal/workflow/WorkflowMethod.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333
@Retention(RetentionPolicy.RUNTIME)
3434
@Target(ElementType.METHOD)
3535
public @interface WorkflowMethod {
36-
/** Name of the workflow type. Default is {short class name} */
36+
/**
37+
* Name of the workflow type. Default is {short class name}.
38+
*
39+
* <p>Be careful with names that contain special characters, as these names can be used as metric
40+
* tags. Systems like Prometheus ignore metrics which have tags with unsupported characters.
41+
*
42+
* <p>Name cannot start with __temporal_ as it is reserved for internal use.
43+
*/
3744
String name() default "";
3845
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
3+
*
4+
* Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this material except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package io.temporal.workflow;
22+
23+
import io.temporal.testing.internal.SDKTestWorkflowRule;
24+
import org.junit.Assert;
25+
import org.junit.Rule;
26+
import org.junit.Test;
27+
28+
public class WorkflowRestrictedNameTest {
29+
30+
@Rule
31+
public SDKTestWorkflowRule testWorkflowRule =
32+
SDKTestWorkflowRule.newBuilder().setDoNotStart(true).build();
33+
34+
@Test
35+
public void testRegisteringRestrictedWorkflowMethod() {
36+
IllegalArgumentException e =
37+
Assert.assertThrows(
38+
IllegalArgumentException.class,
39+
() ->
40+
testWorkflowRule
41+
.getWorker()
42+
.registerWorkflowImplementationTypes(WorkflowMethodWithOverrideNameImpl.class));
43+
Assert.assertEquals(
44+
"workflow name \"__temporal_workflow\" must not start with \"__temporal_\"",
45+
e.getMessage());
46+
47+
e =
48+
Assert.assertThrows(
49+
IllegalArgumentException.class,
50+
() ->
51+
testWorkflowRule
52+
.getWorker()
53+
.registerWorkflowImplementationTypes(WorkflowMethodRestrictedImpl.class));
54+
Assert.assertEquals(
55+
"workflow name \"__temporal_workflow\" must not start with \"__temporal_\"",
56+
e.getMessage());
57+
}
58+
59+
@WorkflowInterface
60+
public interface WorkflowMethodWithOverrideNameRestricted {
61+
@WorkflowMethod(name = "__temporal_workflow")
62+
void workflowMethod();
63+
}
64+
65+
public static class WorkflowMethodWithOverrideNameImpl
66+
implements WorkflowMethodWithOverrideNameRestricted {
67+
68+
@Override
69+
public void workflowMethod() {}
70+
}
71+
72+
@WorkflowInterface
73+
public interface __temporal_workflow {
74+
@WorkflowMethod
75+
void workflowMethod();
76+
}
77+
78+
public static class WorkflowMethodRestrictedImpl implements __temporal_workflow {
79+
@Override
80+
public void workflowMethod() {}
81+
}
82+
}

0 commit comments

Comments
 (0)