Skip to content

Commit 661cae5

Browse files
authored
Durable Task Scheduler SDK Support (#201)
1 parent 82868d7 commit 661cae5

27 files changed

+2502
-9
lines changed

.github/workflows/build-validation.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,25 @@ jobs:
4242
uses: gradle/gradle-build-action@v2
4343

4444
- name: Build with Gradle
45-
run: ./gradlew build
45+
run: ./gradlew build -x test
46+
47+
- name: Run Unit Tests with Gradle
48+
run: |
49+
export JAVA_HOME=$JDK_11
50+
./gradlew clean test || echo "UNIT_TEST_FAILED=true" >> $GITHUB_ENV
51+
continue-on-error: true
52+
53+
- name: Upload test reports if tests failed
54+
if: env.UNIT_TEST_FAILED == 'true'
55+
uses: actions/upload-artifact@v4
56+
with:
57+
name: Unit Test Reports
58+
path: '**/build/reports/tests/test'
59+
if-no-files-found: ignore # Prevents errors if no reports exist
60+
61+
- name: Fail the job if unit tests failed
62+
if: env.UNIT_TEST_FAILED == 'true'
63+
run: exit 1
4664

4765
# TODO: Move the sidecar into a central image repository
4866
- name: Initialize Durable Task Sidecar

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ build/
66
.project
77
.settings
88
.classpath
9-
repo/
9+
repo/
10+
11+
# Ignore sample application properties or any other properties files used for sample
12+
samples/src/main/resources/*.properties

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## placeholder
2+
* DTS Support ([#201](https://github.com/microsoft/durabletask-java/pull/201))
23
* Add automatic proto file download and commit hash tracking during build ([#207](https://github.com/microsoft/durabletask-java/pull/207))
34
* Fix infinite loop when use continueasnew after wait external event ([#183](https://github.com/microsoft/durabletask-java/pull/183))
45
* Fix the issue "Deserialize Exception got swallowed when use anyOf with external event." ([#185](https://github.com/microsoft/durabletask-java/pull/185))

azuremanaged/build.gradle

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* This build file was generated by the Gradle 'init' task.
3+
*
4+
* This generated file contains a sample Java project to get you started.
5+
* For more details take a look at the Java Quickstart chapter in the Gradle
6+
* user guide available at https://docs.gradle.org/4.4.1/userguide/tutorial_java_projects.html
7+
*/
8+
9+
plugins {
10+
id 'java'
11+
id 'com.google.protobuf' version '0.8.16'
12+
id 'idea'
13+
id 'maven-publish'
14+
id 'signing'
15+
}
16+
17+
archivesBaseName = 'durabletask-azuremanaged'
18+
group 'com.microsoft'
19+
version = '1.5.0-preview.1'
20+
21+
def grpcVersion = '1.59.0'
22+
def azureCoreVersion = '1.45.0'
23+
def azureIdentityVersion = '1.11.1'
24+
// When build on local, you need to set this value to your local jdk11 directory.
25+
// Java11 is used to compile and run all the tests.
26+
def PATH_TO_TEST_JAVA_RUNTIME = System.env.JDK_11 ?: System.getProperty("java.home")
27+
28+
repositories {
29+
mavenCentral()
30+
}
31+
32+
dependencies {
33+
implementation project(':client')
34+
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
35+
implementation "io.grpc:grpc-stub:${grpcVersion}"
36+
runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
37+
38+
implementation "com.azure:azure-core:${azureCoreVersion}"
39+
implementation "com.azure:azure-identity:${azureIdentityVersion}"
40+
41+
// Test dependencies
42+
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
43+
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0'
44+
testImplementation 'org.mockito:mockito-core:5.3.1'
45+
testImplementation 'org.mockito:mockito-junit-jupiter:5.3.1'
46+
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
47+
}
48+
49+
compileJava {
50+
sourceCompatibility = JavaVersion.VERSION_1_8
51+
targetCompatibility = JavaVersion.VERSION_1_8
52+
}
53+
54+
compileTestJava {
55+
sourceCompatibility = JavaVersion.VERSION_11
56+
targetCompatibility = JavaVersion.VERSION_11
57+
options.fork = true
58+
options.forkOptions.executable = "${PATH_TO_TEST_JAVA_RUNTIME}/bin/javac"
59+
}
60+
61+
test {
62+
useJUnitPlatform()
63+
}
64+
65+
publishing {
66+
repositories {
67+
maven {
68+
url "file://$project.rootDir/repo"
69+
}
70+
}
71+
publications {
72+
mavenJava(MavenPublication) {
73+
from components.java
74+
artifactId = archivesBaseName
75+
pom {
76+
name = 'Durable Task Azure Managed SDK for Java'
77+
description = 'This package contains classes and interfaces for building Durable Task orchestrations in Java using Azure Managed mode.'
78+
url = "https://github.com/microsoft/durabletask-java/tree/main/azuremanaged"
79+
licenses {
80+
license {
81+
name = "MIT License"
82+
url = "https://opensource.org/licenses/MIT"
83+
distribution = "repo"
84+
}
85+
}
86+
developers {
87+
developer {
88+
id = "Microsoft"
89+
name = "Microsoft Corporation"
90+
}
91+
}
92+
scm {
93+
connection = "scm:git:https://github.com/microsoft/durabletask-java"
94+
developerConnection = "scm:git:[email protected]:microsoft/durabletask-java"
95+
url = "https://github.com/microsoft/durabletask-java/tree/main/azuremanaged"
96+
}
97+
withXml {
98+
project.configurations.compileOnly.allDependencies.each { dependency ->
99+
asNode().dependencies[0].appendNode("dependency").with {
100+
it.appendNode("groupId", dependency.group)
101+
it.appendNode("artifactId", dependency.name)
102+
it.appendNode("version", dependency.version)
103+
it.appendNode("scope", "provided")
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}
110+
}
111+
112+
java {
113+
withSourcesJar()
114+
withJavadocJar()
115+
}
116+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.durabletask.azuremanaged;
5+
6+
import com.azure.core.credential.TokenCredential;
7+
import com.azure.core.credential.AccessToken;
8+
import com.azure.core.credential.TokenRequestContext;
9+
10+
import java.time.Duration;
11+
import java.time.OffsetDateTime;
12+
13+
/**
14+
* Caches access tokens for Azure authentication.
15+
* This class is used by both client and worker components to authenticate with Azure-managed Durable Task Scheduler.
16+
*/
17+
public final class AccessTokenCache {
18+
private final TokenCredential credential;
19+
private final TokenRequestContext context;
20+
private final Duration margin;
21+
private AccessToken cachedToken;
22+
23+
/**
24+
* Creates a new instance of the AccessTokenCache.
25+
*
26+
* @param credential The token credential to use for obtaining tokens.
27+
* @param context The token request context specifying the scopes.
28+
* @param margin The time margin before token expiration to refresh the token.
29+
*/
30+
public AccessTokenCache(TokenCredential credential, TokenRequestContext context, Duration margin) {
31+
this.credential = credential;
32+
this.context = context;
33+
this.margin = margin;
34+
}
35+
36+
/**
37+
* Gets a valid access token, refreshing it if necessary.
38+
*
39+
* @return A valid access token.
40+
*/
41+
public AccessToken getToken() {
42+
OffsetDateTime nowWithMargin = OffsetDateTime.now().plus(margin);
43+
44+
if (cachedToken == null
45+
|| cachedToken.getExpiresAt().isBefore(nowWithMargin)) {
46+
this.cachedToken = credential.getToken(context).block();
47+
}
48+
49+
return cachedToken;
50+
}
51+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.durabletask.azuremanaged;
5+
6+
import com.microsoft.durabletask.DurableTaskGrpcClientBuilder;
7+
import com.azure.core.credential.TokenCredential;
8+
import io.grpc.Channel;
9+
import java.util.Objects;
10+
import javax.annotation.Nullable;
11+
12+
/**
13+
* Extension methods for creating DurableTaskClient instances that connect to Azure-managed Durable Task Scheduler.
14+
* This class provides various methods to create and configure clients using either connection strings or explicit parameters.
15+
*/
16+
public final class DurableTaskSchedulerClientExtensions {
17+
18+
private DurableTaskSchedulerClientExtensions() {}
19+
20+
/**
21+
* Configures a DurableTaskGrpcClientBuilder to use Azure-managed Durable Task Scheduler with a connection string.
22+
*
23+
* @param builder The builder to configure.
24+
* @param connectionString The connection string for Azure-managed Durable Task Scheduler.
25+
* @throws NullPointerException if builder or connectionString is null
26+
*/
27+
public static void useDurableTaskScheduler(
28+
DurableTaskGrpcClientBuilder builder,
29+
String connectionString) {
30+
Objects.requireNonNull(builder, "builder must not be null");
31+
Objects.requireNonNull(connectionString, "connectionString must not be null");
32+
33+
configureBuilder(builder,
34+
DurableTaskSchedulerClientOptions.fromConnectionString(connectionString));
35+
}
36+
37+
/**
38+
* Configures a DurableTaskGrpcClientBuilder to use Azure-managed Durable Task Scheduler with explicit parameters.
39+
*
40+
* @param builder The builder to configure.
41+
* @param endpoint The endpoint address for Azure-managed Durable Task Scheduler.
42+
* @param taskHubName The name of the task hub to connect to.
43+
* @param tokenCredential The token credential for authentication, or null for anonymous access.
44+
* @throws NullPointerException if builder, endpoint, or taskHubName is null
45+
*/
46+
public static void useDurableTaskScheduler(
47+
DurableTaskGrpcClientBuilder builder,
48+
String endpoint,
49+
String taskHubName,
50+
@Nullable TokenCredential tokenCredential) {
51+
Objects.requireNonNull(builder, "builder must not be null");
52+
Objects.requireNonNull(endpoint, "endpoint must not be null");
53+
Objects.requireNonNull(taskHubName, "taskHubName must not be null");
54+
55+
configureBuilder(builder, new DurableTaskSchedulerClientOptions()
56+
.setEndpointAddress(endpoint)
57+
.setTaskHubName(taskHubName)
58+
.setCredential(tokenCredential));
59+
}
60+
61+
/**
62+
* Creates a DurableTaskGrpcClientBuilder configured for Azure-managed Durable Task Scheduler using a connection string.
63+
*
64+
* @param connectionString The connection string for Azure-managed Durable Task Scheduler.
65+
* @return A new configured DurableTaskGrpcClientBuilder instance.
66+
* @throws NullPointerException if connectionString is null
67+
*/
68+
public static DurableTaskGrpcClientBuilder createClientBuilder(
69+
String connectionString) {
70+
Objects.requireNonNull(connectionString, "connectionString must not be null");
71+
return createBuilderFromOptions(
72+
DurableTaskSchedulerClientOptions.fromConnectionString(connectionString));
73+
}
74+
75+
/**
76+
* Creates a DurableTaskGrpcClientBuilder configured for Azure-managed Durable Task Scheduler using explicit parameters.
77+
*
78+
* @param endpoint The endpoint address for Azure-managed Durable Task Scheduler.
79+
* @param taskHubName The name of the task hub to connect to.
80+
* @param tokenCredential The token credential for authentication, or null for anonymous access.
81+
* @return A new configured DurableTaskGrpcClientBuilder instance.
82+
* @throws NullPointerException if endpoint or taskHubName is null
83+
*/
84+
public static DurableTaskGrpcClientBuilder createClientBuilder(
85+
String endpoint,
86+
String taskHubName,
87+
@Nullable TokenCredential tokenCredential) {
88+
Objects.requireNonNull(endpoint, "endpoint must not be null");
89+
Objects.requireNonNull(taskHubName, "taskHubName must not be null");
90+
91+
return createBuilderFromOptions(new DurableTaskSchedulerClientOptions()
92+
.setEndpointAddress(endpoint)
93+
.setTaskHubName(taskHubName)
94+
.setCredential(tokenCredential));
95+
}
96+
97+
// Private helper methods to reduce code duplication
98+
private static DurableTaskGrpcClientBuilder createBuilderFromOptions(DurableTaskSchedulerClientOptions options) {
99+
Channel grpcChannel = options.createGrpcChannel();
100+
return new DurableTaskGrpcClientBuilder().grpcChannel(grpcChannel);
101+
}
102+
103+
private static void configureBuilder(DurableTaskGrpcClientBuilder builder, DurableTaskSchedulerClientOptions options) {
104+
Channel grpcChannel = options.createGrpcChannel();
105+
builder.grpcChannel(grpcChannel);
106+
}
107+
}

0 commit comments

Comments
 (0)