Skip to content

Commit ab50230

Browse files
committed
Merge branch 'main' into vabachu/java-large-payloads
2 parents 4dd2e93 + dd2564b commit ab50230

40 files changed

Lines changed: 1994 additions & 41 deletions

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## Unreleased
2-
* Add work item filtering support for `DurableTaskGrpcWorker` to enable worker-side filtering of orchestration and activity work items ([#275](https://github.com/microsoft/durabletask-java/pull/275))
2+
3+
## v1.9.0
4+
* Fix entity locking deserialization and add Jackson support for EntityInstanceId/EntityMetadata ([#281](https://github.com/microsoft/durabletask-java/pull/281))
5+
* Durable Entities ([#268](https://github.com/microsoft/durabletask-java/pull/268))
6+
* Add work item filtering support for `DurableTaskGrpcWorker` to enable worker-side filtering of orchestration and activity work items ([#275](https://github.com/microsoft/durabletask-java/pull/275))
37
* Add support for calls to HTTP endpoints ([#271](https://github.com/microsoft/durabletask-java/pull/271))
48
* Add getSuspendPostUri and getResumePostUri getters to HttpManagementPayload ([#264](https://github.com/microsoft/durabletask-java/pull/264))
59

azurefunctions/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66
}
77

88
group 'com.microsoft'
9-
version = '1.8.0'
9+
version = '1.9.0'
1010
archivesBaseName = 'durabletask-azure-functions'
1111

1212
def protocVersion = '3.25.8'

azuremanaged/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ plugins {
1717

1818
archivesBaseName = 'durabletask-azuremanaged'
1919
group 'com.microsoft'
20-
version = '1.8.0'
20+
version = '1.9.0'
2121

2222
def grpcVersion = '1.78.0'
2323
def azureCoreVersion = '1.57.1'

client/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ plugins {
1010
}
1111

1212
group 'com.microsoft'
13-
version = '1.8.0'
13+
version = '1.9.0'
1414
archivesBaseName = 'durabletask-client'
1515

1616
def grpcVersion = '1.78.0'

client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcWorker.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public final class DurableTaskGrpcWorker implements AutoCloseable {
4848
private final DataConverter dataConverter;
4949
private final Duration maximumTimerInterval;
5050
private final DurableTaskGrpcWorkerVersioningOptions versioningOptions;
51+
private final ExceptionPropertiesProvider exceptionPropertiesProvider;
5152
private final int maxConcurrentEntityWorkItems;
5253
private final ExecutorService workItemExecutor;
5354

@@ -97,6 +98,7 @@ public final class DurableTaskGrpcWorker implements AutoCloseable {
9798
this.dataConverter = builder.dataConverter != null ? builder.dataConverter : new JacksonDataConverter();
9899
this.maximumTimerInterval = builder.maximumTimerInterval != null ? builder.maximumTimerInterval : DEFAULT_MAXIMUM_TIMER_INTERVAL;
99100
this.versioningOptions = builder.versioningOptions;
101+
this.exceptionPropertiesProvider = builder.exceptionPropertiesProvider;
100102
int maxThreads = builder.maxWorkItemThreads > 0 ? builder.maxWorkItemThreads : DEFAULT_MAX_WORK_ITEM_THREADS;
101103
this.workItemExecutor = new ThreadPoolExecutor(
102104
0, maxThreads,
@@ -172,7 +174,8 @@ public void startAndBlock() {
172174
this.maximumTimerInterval,
173175
logger,
174176
this.versioningOptions,
175-
true);
177+
true,
178+
this.exceptionPropertiesProvider);
176179
TaskActivityExecutor taskActivityExecutor = new TaskActivityExecutor(
177180
this.activityFactories,
178181
this.dataConverter,
@@ -402,11 +405,8 @@ public void startAndBlock() {
402405
activityRequest.getTaskId());
403406
} catch (Throwable e) {
404407
activityError = e;
405-
failureDetails = TaskFailureDetails.newBuilder()
406-
.setErrorType(e.getClass().getName())
407-
.setErrorMessage(e.getMessage())
408-
.setStackTrace(StringValue.of(FailureDetails.getFullStackTrace(e)))
409-
.build();
408+
failureDetails = FailureDetails.fromException(
409+
e, this.exceptionPropertiesProvider).toProto();
410410
} finally {
411411
activityScope.close();
412412
TracingHelper.endSpan(activitySpan, activityError);

client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcWorkerBuilder.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public final class DurableTaskGrpcWorkerBuilder {
2525
DataConverter dataConverter;
2626
Duration maximumTimerInterval;
2727
DurableTaskGrpcWorkerVersioningOptions versioningOptions;
28+
ExceptionPropertiesProvider exceptionPropertiesProvider;
2829
int maxConcurrentEntityWorkItems = 1;
2930
int maxWorkItemThreads;
3031
private WorkItemFilter workItemFilter;
@@ -314,6 +315,21 @@ public DurableTaskGrpcWorkerBuilder useVersioning(DurableTaskGrpcWorkerVersionin
314315
return this;
315316
}
316317

318+
/**
319+
* Sets the {@link ExceptionPropertiesProvider} to use for extracting custom properties from exceptions.
320+
* <p>
321+
* When set, the provider is invoked whenever an activity or orchestration fails with an exception. The returned
322+
* properties are included in the {@link FailureDetails} and can be retrieved via
323+
* {@link FailureDetails#getProperties()}.
324+
*
325+
* @param provider the exception properties provider
326+
* @return this builder object
327+
*/
328+
public DurableTaskGrpcWorkerBuilder exceptionPropertiesProvider(ExceptionPropertiesProvider provider) {
329+
this.exceptionPropertiesProvider = provider;
330+
return this;
331+
}
332+
317333
/**
318334
* Sets explicit work item filters for this worker. When set, only work items matching the filters
319335
* will be dispatched to this worker by the backend.

client/src/main/java/com/microsoft/durabletask/EntityInstanceId.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,17 @@
22
// Licensed under the MIT License.
33
package com.microsoft.durabletask;
44

5+
import com.fasterxml.jackson.core.JsonGenerator;
6+
import com.fasterxml.jackson.core.JsonParser;
7+
import com.fasterxml.jackson.databind.DeserializationContext;
8+
import com.fasterxml.jackson.databind.JsonDeserializer;
9+
import com.fasterxml.jackson.databind.JsonSerializer;
10+
import com.fasterxml.jackson.databind.SerializerProvider;
11+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
12+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
13+
514
import javax.annotation.Nonnull;
15+
import java.io.IOException;
616
import java.util.Locale;
717
import java.util.Objects;
818

@@ -11,7 +21,12 @@
1121
* <p>
1222
* The name typically corresponds to the entity class/type name, and the key identifies the specific
1323
* entity instance (e.g., a user ID or account number).
24+
* <p>
25+
* Serializes to and deserializes from a compact string format {@code @{name}@{key}},
26+
* matching the .NET SDK's {@code EntityInstanceId} JSON representation.
1427
*/
28+
@JsonSerialize(using = EntityInstanceId.Serializer.class)
29+
@JsonDeserialize(using = EntityInstanceId.Deserializer.class)
1530
public final class EntityInstanceId implements Comparable<EntityInstanceId> {
1631
private final String name;
1732
private final String key;
@@ -116,4 +131,26 @@ public int compareTo(@Nonnull EntityInstanceId other) {
116131
}
117132
return this.key.compareTo(other.key);
118133
}
134+
135+
/**
136+
* Jackson serializer that writes an {@code EntityInstanceId} as a compact {@code "@name@key"} string.
137+
*/
138+
static class Serializer extends JsonSerializer<EntityInstanceId> {
139+
@Override
140+
public void serialize(EntityInstanceId value, JsonGenerator gen, SerializerProvider serializers)
141+
throws IOException {
142+
gen.writeString(value.toString());
143+
}
144+
}
145+
146+
/**
147+
* Jackson deserializer that reads an {@code EntityInstanceId} from a compact {@code "@name@key"} string.
148+
*/
149+
static class Deserializer extends JsonDeserializer<EntityInstanceId> {
150+
@Override
151+
public EntityInstanceId deserialize(JsonParser p, DeserializationContext ctxt)
152+
throws IOException {
153+
return EntityInstanceId.fromString(p.getText());
154+
}
155+
}
119156
}

client/src/main/java/com/microsoft/durabletask/EntityMetadata.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// Licensed under the MIT License.
33
package com.microsoft.durabletask;
44

5+
import com.fasterxml.jackson.annotation.JsonIgnore;
6+
import com.fasterxml.jackson.annotation.JsonProperty;
7+
58
import javax.annotation.Nullable;
69
import java.time.Instant;
710

@@ -18,8 +21,10 @@ public class EntityMetadata {
1821
private final Instant lastModifiedTime;
1922
private final int backlogQueueSize;
2023
private final String lockedBy;
24+
@JsonIgnore
2125
private final String serializedState;
2226
private final boolean includesState;
27+
@JsonIgnore
2328
private final DataConverter dataConverter;
2429
private volatile EntityInstanceId cachedEntityInstanceId;
2530

@@ -56,6 +61,7 @@ public class EntityMetadata {
5661
*
5762
* @return the instance ID
5863
*/
64+
@JsonIgnore
5965
public String getInstanceId() {
6066
return this.instanceId;
6167
}
@@ -65,6 +71,7 @@ public String getInstanceId() {
6571
*
6672
* @return the parsed entity instance ID
6773
*/
74+
@JsonProperty("entityId")
6875
public EntityInstanceId getEntityInstanceId() {
6976
if (this.cachedEntityInstanceId == null) {
7077
this.cachedEntityInstanceId = EntityInstanceId.fromString(this.instanceId);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.microsoft.durabletask;
4+
5+
import javax.annotation.Nullable;
6+
import java.util.Map;
7+
8+
/**
9+
* Provider interface for extracting custom properties from exceptions.
10+
* <p>
11+
* Implementations of this interface can be registered with a {@link DurableTaskGrpcWorkerBuilder} to include
12+
* custom exception properties in {@link FailureDetails} when activities or orchestrations fail.
13+
* These properties are then available via {@link FailureDetails#getProperties()}.
14+
* <p>
15+
* Example usage:
16+
* <pre>{@code
17+
* DurableTaskGrpcWorker worker = new DurableTaskGrpcWorkerBuilder()
18+
* .exceptionPropertiesProvider(exception -> {
19+
* if (exception instanceof MyCustomException) {
20+
* MyCustomException custom = (MyCustomException) exception;
21+
* Map<String, Object> props = new HashMap<>();
22+
* props.put("errorCode", custom.getErrorCode());
23+
* props.put("retryable", custom.isRetryable());
24+
* return props;
25+
* }
26+
* return null;
27+
* })
28+
* .addOrchestration(...)
29+
* .build();
30+
* }</pre>
31+
*/
32+
@FunctionalInterface
33+
public interface ExceptionPropertiesProvider {
34+
35+
/**
36+
* Extracts custom properties from the given exception.
37+
* <p>
38+
* Return {@code null} or an empty map if no custom properties should be included for this exception.
39+
*
40+
* @param exception the exception to extract properties from
41+
* @return a map of property names to values, or {@code null}
42+
*/
43+
@Nullable
44+
Map<String, Object> getExceptionProperties(Exception exception);
45+
}

0 commit comments

Comments
 (0)