mirror of https://github.com/dapr/java-sdk.git
Add the Jobs SDK (#1255)
* Add jobs Signed-off-by: sirivarma <siri.varma@outlook.com> * Add validations Signed-off-by: sirivarma <siri.varma@outlook.com> * Add things Signed-off-by: sirivarma <siri.varma@outlook.com> * Add things Signed-off-by: sirivarma <siri.varma@outlook.com> * Remove builder and change to setter Signed-off-by: sirivarma <siri.varma@outlook.com> * Remove module Signed-off-by: sirivarma <siri.varma@outlook.com> * remove jobs Signed-off-by: sirivarma <siri.varma@outlook.com> * change bean name Signed-off-by: sirivarma <siri.varma@outlook.com> * Use latest Dapr release Signed-off-by: artur-ciocanu <artur.ciocanu@gmail.com> * fix things Signed-off-by: siri-varma <siri.varma@outlook.com> * Fix comments and fix tests Signed-off-by: siri-varma <siri.varma@outlook.com> * remove * Signed-off-by: siri-varma <siri.varma@outlook.com> * fix conflicts Signed-off-by: siri-varma <siri.varma@outlook.com> * remove space Signed-off-by: siri-varma <siri.varma@outlook.com> * Update sdk-tests/src/test/java/io/dapr/it/testcontainers/DaprJobsIT.java Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Update testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Add comment Signed-off-by: siri-varma <siri.varma@outlook.com> * Update DaprPreviewClientGrpcTest.java Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Update DaprPreviewClientGrpcTest.java Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Fix spaces Signed-off-by: siri-varma <siri.varma@outlook.com> * Fix spaces Signed-off-by: siri-varma <siri.varma@outlook.com> * fixt hings Signed-off-by: siri-varma <siri.varma@outlook.com> * Add examples Signed-off-by: siri-varma <siri.varma@outlook.com> * Cleanup Signed-off-by: siri-varma <siri.varma@outlook.com> * indent to spaces Signed-off-by: siri-varma <siri.varma@outlook.com> * Update README.md Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Update README.md Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Update DemoJobsClient.java Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Update DaprClientImpl.java Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Update DaprClientImpl.java Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> --------- Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: artur-ciocanu <artur.ciocanu@gmail.com> Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> Co-authored-by: artur-ciocanu <artur.ciocanu@gmail.com> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com>
This commit is contained in:
parent
128cfdeb4b
commit
ed9a3fb77b
|
@ -115,6 +115,12 @@ jobs:
|
|||
run: ./mvnw install -q
|
||||
env:
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
- name: Validate Jobs example
|
||||
working-directory: ./examples
|
||||
run: |
|
||||
mm.py ./src/main/java/io/dapr/examples/jobs/README.md
|
||||
env:
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
- name: Validate invoke http example
|
||||
working-directory: ./examples
|
||||
run: |
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2021 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.jobs;
|
||||
|
||||
import io.dapr.client.DaprClientBuilder;
|
||||
import io.dapr.client.DaprPreviewClient;
|
||||
import io.dapr.client.domain.GetJobRequest;
|
||||
import io.dapr.client.domain.GetJobResponse;
|
||||
import io.dapr.client.domain.JobSchedule;
|
||||
import io.dapr.client.domain.ScheduleJobRequest;
|
||||
import io.dapr.config.Properties;
|
||||
import io.dapr.config.Property;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class DemoJobsClient {
|
||||
|
||||
/**
|
||||
* The main method of this app to register and fetch jobs.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
Map<Property<?>, String> overrides = Map.of(
|
||||
Properties.HTTP_PORT, "3500",
|
||||
Properties.GRPC_PORT, "51439"
|
||||
);
|
||||
|
||||
try (DaprPreviewClient client = new DaprClientBuilder().withPropertyOverrides(overrides).buildPreviewClient()) {
|
||||
|
||||
// Schedule a job.
|
||||
System.out.println("**** Scheduling a Job with name dapr-jobs-1 *****");
|
||||
ScheduleJobRequest scheduleJobRequest = new ScheduleJobRequest("dapr-job-1",
|
||||
JobSchedule.fromString("* * * * * *")).setData("Hello World!".getBytes());
|
||||
client.scheduleJob(scheduleJobRequest).block();
|
||||
|
||||
System.out.println("**** Scheduling job dapr-jobs-1 completed *****");
|
||||
|
||||
// Get a job.
|
||||
System.out.println("**** Retrieving a Job with name dapr-jobs-1 *****");
|
||||
GetJobResponse getJobResponse = client.getJob(new GetJobRequest("dapr-job-1")).block();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2021 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.jobs;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Spring Boot application to demonstrate Dapr Jobs callback API.
|
||||
* <p>
|
||||
* This application demonstrates how to use Dapr Jobs API with Spring Boot.
|
||||
* </p>
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class DemoJobsSpringApplication {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(DemoJobsSpringApplication.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2021 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.jobs;
|
||||
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* SpringBoot Controller to handle jobs callback.
|
||||
*/
|
||||
@RestController
|
||||
public class JobsController {
|
||||
|
||||
/**
|
||||
* Handles jobs callback from Dapr.
|
||||
*
|
||||
* @param jobName name of the job.
|
||||
* @param payload data from the job if payload exists.
|
||||
* @return Empty Mono.
|
||||
*/
|
||||
@PostMapping("/job/{jobName}")
|
||||
public Mono<Void> handleJob(@PathVariable("jobName") String jobName,
|
||||
@RequestBody(required = false) byte[] payload) {
|
||||
System.out.println("Job Name: " + jobName);
|
||||
System.out.println("Job Payload: " + new String(payload));
|
||||
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
## Manage Dapr Jobs via the Jobs API
|
||||
|
||||
This example provides the different capabilities provided by Dapr Java SDK for Jobs. For further information about Job APIs please refer to [this link](https://docs.dapr.io/developing-applications/building-blocks/jobs/jobs-overview/)
|
||||
|
||||
### Using the Jobs API
|
||||
|
||||
The Java SDK exposes several methods for this -
|
||||
* `client.scheduleJob(...)` for scheduling a job.
|
||||
* `client.getJob(...)` for retrieving a scheduled job.
|
||||
* `client.deleteJob(...)` for deleting a job.
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
* [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/).
|
||||
* Java JDK 11 (or greater):
|
||||
* [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11)
|
||||
* [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11)
|
||||
* [OpenJDK 11](https://jdk.java.net/11/)
|
||||
* [Apache Maven](https://maven.apache.org/install.html) version 3.x.
|
||||
|
||||
### Checking out the code
|
||||
|
||||
Clone this repository:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/dapr/java-sdk.git
|
||||
cd java-sdk
|
||||
```
|
||||
|
||||
Then build the Maven project:
|
||||
|
||||
```sh
|
||||
# make sure you are in the `java-sdk` directory.
|
||||
mvn install
|
||||
```
|
||||
|
||||
Then get into the examples directory:
|
||||
|
||||
```sh
|
||||
cd examples
|
||||
```
|
||||
|
||||
### Initialize Dapr
|
||||
|
||||
Run `dapr init` to initialize Dapr in Self-Hosted Mode if it's not already initialized.
|
||||
|
||||
### Running the example
|
||||
|
||||
This example uses the Java SDK Dapr client in order to **Schedule and Get** Jobs.
|
||||
`DemoJobsClient.java` is the example class demonstrating these features.
|
||||
Kindly check [DaprPreviewClient.java](https://github.com/dapr/java-sdk/blob/master/sdk/src/main/java/io/dapr/client/DaprPreviewClient.java) for a detailed description of the supported APIs.
|
||||
|
||||
```java
|
||||
public class DemoJobsClient {
|
||||
/**
|
||||
* The main method of this app to register and fetch jobs.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
Map<Property<?>, String> overrides = Map.of(
|
||||
Properties.HTTP_PORT, "3500",
|
||||
Properties.GRPC_PORT, "51439"
|
||||
);
|
||||
|
||||
try (DaprPreviewClient client = new DaprClientBuilder().withPropertyOverrides(overrides).buildPreviewClient()) {
|
||||
|
||||
// Schedule a job.
|
||||
ScheduleJobRequest scheduleJobRequest = new ScheduleJobRequest("dapr-job-1",
|
||||
JobSchedule.fromString("* * * * * *")).setData("Hello World!".getBytes());
|
||||
client.scheduleJob(scheduleJobRequest).block();
|
||||
|
||||
// Get a job.
|
||||
GetJobResponse getJobResponse = client.getJob(new GetJobRequest("dapr-job-1")).block();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use the following command to run this example-
|
||||
|
||||
<!-- STEP
|
||||
name: Run Demo Jobs Client example
|
||||
expected_stdout_lines:
|
||||
- "== APP == Job Name: dapr-job-1"
|
||||
- "== APP == Job Payload: Hello World!"
|
||||
background: true
|
||||
output_match_mode: substring
|
||||
sleep: 10
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run --resources-path ./components/configuration --app-id myapp --app-port 8080 --dapr-http-port 3500 --dapr-grpc-port 51439 --log-level debug -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.jobs.DemoJobsSpringApplication
|
||||
```
|
||||
|
||||
```bash
|
||||
java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.jobs.DemoJobsClient
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
### Sample output
|
||||
```
|
||||
== APP == Job Name: dapr-job-1
|
||||
== APP == Job Payload: Hello World!
|
||||
```
|
||||
### Cleanup
|
||||
|
||||
To stop the app, run (or press CTRL+C):
|
||||
|
||||
<!-- STEP
|
||||
name: Cleanup
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop --app-id myapp
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
2
pom.xml
2
pom.xml
|
@ -17,7 +17,7 @@
|
|||
<grpc.version>1.69.0</grpc.version>
|
||||
<protobuf.version>3.25.5</protobuf.version>
|
||||
<protocCommand>protoc</protocCommand>
|
||||
<dapr.proto.baseurl>https://raw.githubusercontent.com/dapr/dapr/v1.14.4/dapr/proto</dapr.proto.baseurl>
|
||||
<dapr.proto.baseurl>https://raw.githubusercontent.com/dapr/dapr/v1.15.3/dapr/proto</dapr.proto.baseurl>
|
||||
<dapr.sdk.version>1.15.0-SNAPSHOT</dapr.sdk.version>
|
||||
<dapr.sdk.alpha.version>0.15.0-SNAPSHOT</dapr.sdk.alpha.version>
|
||||
<os-maven-plugin.version>1.7.1</os-maven-plugin.version>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package io.dapr.it.testcontainers;
|
||||
|
||||
public interface ContainerConstants {
|
||||
String DAPR_IMAGE_TAG = "daprio/daprd:1.14.1";
|
||||
String TOXIPROXY_IMAGE_TAG = "ghcr.io/shopify/toxiproxy:2.5.0";
|
||||
String DAPR_RUNTIME_VERSION = "1.15.3";
|
||||
String DAPR_IMAGE_TAG = "daprio/daprd:" + DAPR_RUNTIME_VERSION;
|
||||
String DAPR_PLACEMENT_IMAGE_TAG = "daprio/placement:" + DAPR_RUNTIME_VERSION;
|
||||
String DAPR_SCHEDULER_IMAGE_TAG = "daprio/scheduler:" + DAPR_RUNTIME_VERSION;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
|
||||
import io.dapr.client.DaprPreviewClient;
|
||||
import io.dapr.client.domain.DeleteJobRequest;
|
||||
import io.dapr.client.domain.GetJobRequest;
|
||||
import io.dapr.client.domain.GetJobResponse;
|
||||
import io.dapr.client.domain.JobSchedule;
|
||||
import io.dapr.client.domain.ScheduleJobRequest;
|
||||
import io.dapr.testcontainers.DaprContainer;
|
||||
import io.dapr.testcontainers.DaprLogLevel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
import org.springframework.test.context.DynamicPropertySource;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
classes = {
|
||||
TestDaprJobsConfiguration.class,
|
||||
TestJobsApplication.class
|
||||
}
|
||||
)
|
||||
@Testcontainers
|
||||
@Tag("testcontainers")
|
||||
public class DaprJobsIT {
|
||||
|
||||
private static final Network DAPR_NETWORK = Network.newNetwork();
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final int PORT = RANDOM.nextInt(1000) + 8000;
|
||||
|
||||
@Container
|
||||
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(ContainerConstants.DAPR_IMAGE_TAG)
|
||||
.withAppName("jobs-dapr-app")
|
||||
.withNetwork(DAPR_NETWORK)
|
||||
.withDaprLogLevel(DaprLogLevel.DEBUG)
|
||||
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
|
||||
.withAppChannelAddress("host.testcontainers.internal")
|
||||
.withAppPort(PORT);
|
||||
|
||||
/**
|
||||
* Expose the Dapr ports to the host.
|
||||
*
|
||||
* @param registry the dynamic property registry
|
||||
*/
|
||||
@DynamicPropertySource
|
||||
static void daprProperties(DynamicPropertyRegistry registry) {
|
||||
registry.add("dapr.http.endpoint", DAPR_CONTAINER::getHttpEndpoint);
|
||||
registry.add("dapr.grpc.endpoint", DAPR_CONTAINER::getGrpcEndpoint);
|
||||
registry.add("server.port", () -> PORT);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private DaprPreviewClient daprPreviewClient;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(){
|
||||
org.testcontainers.Testcontainers.exposeHostPorts(PORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobScheduleCreationWithDueTime() {
|
||||
DateTimeFormatter iso8601Formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
.withZone(ZoneOffset.UTC);
|
||||
|
||||
Instant currentTime = Instant.now();
|
||||
daprPreviewClient.scheduleJob(new ScheduleJobRequest("Job", currentTime)).block();
|
||||
|
||||
GetJobResponse getJobResponse =
|
||||
daprPreviewClient.getJob(new GetJobRequest("Job")).block();
|
||||
assertEquals(iso8601Formatter.format(currentTime), getJobResponse.getDueTime().toString());
|
||||
assertEquals("Job", getJobResponse.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobScheduleCreationWithSchedule() {
|
||||
DateTimeFormatter iso8601Formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
.withZone(ZoneOffset.UTC);
|
||||
|
||||
Instant currentTime = Instant.now();
|
||||
daprPreviewClient.scheduleJob(new ScheduleJobRequest("Job", JobSchedule.hourly())
|
||||
.setDueTime(currentTime)).block();
|
||||
|
||||
GetJobResponse getJobResponse =
|
||||
daprPreviewClient.getJob(new GetJobRequest("Job")).block();
|
||||
assertEquals(iso8601Formatter.format(currentTime), getJobResponse.getDueTime().toString());
|
||||
assertEquals(JobSchedule.hourly().getExpression(), getJobResponse.getSchedule().getExpression());
|
||||
assertEquals("Job", getJobResponse.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobScheduleCreationWithAllParameters() {
|
||||
Instant currentTime = Instant.now();
|
||||
DateTimeFormatter iso8601Formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
.withZone(ZoneOffset.UTC);
|
||||
|
||||
String cronExpression = "2 * 3 * * FRI";
|
||||
|
||||
daprPreviewClient.scheduleJob(new ScheduleJobRequest("Job", currentTime)
|
||||
.setTtl(currentTime.plus(2, ChronoUnit.HOURS))
|
||||
.setData("Job data".getBytes())
|
||||
.setRepeat(3)
|
||||
.setSchedule(JobSchedule.fromString(cronExpression))).block();
|
||||
|
||||
GetJobResponse getJobResponse =
|
||||
daprPreviewClient.getJob(new GetJobRequest("Job")).block();
|
||||
assertEquals(iso8601Formatter.format(currentTime), getJobResponse.getDueTime().toString());
|
||||
assertEquals("2 * 3 * * FRI", getJobResponse.getSchedule().getExpression());
|
||||
assertEquals("Job", getJobResponse.getName());
|
||||
assertEquals(Integer.valueOf(3), getJobResponse.getRepeats());
|
||||
assertEquals("Job data", new String(getJobResponse.getData()));
|
||||
assertEquals(iso8601Formatter.format(currentTime.plus(2, ChronoUnit.HOURS)),
|
||||
getJobResponse.getTtl().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteJobRequest() {
|
||||
Instant currentTime = Instant.now();
|
||||
|
||||
String cronExpression = "2 * 3 * * FRI";
|
||||
|
||||
daprPreviewClient.scheduleJob(new ScheduleJobRequest("Job", currentTime)
|
||||
.setTtl(currentTime.plus(2, ChronoUnit.HOURS))
|
||||
.setData("Job data".getBytes())
|
||||
.setRepeat(3)
|
||||
.setSchedule(JobSchedule.fromString(cronExpression))).block();
|
||||
|
||||
daprPreviewClient.deleteJob(new DeleteJobRequest("Job")).block();
|
||||
}
|
||||
}
|
|
@ -26,7 +26,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
public class DaprPlacementContainerIT {
|
||||
|
||||
@Container
|
||||
private static final DaprPlacementContainer PLACEMENT_CONTAINER = new DaprPlacementContainer("daprio/placement");
|
||||
private static final DaprPlacementContainer PLACEMENT_CONTAINER =
|
||||
new DaprPlacementContainer(ContainerConstants.DAPR_PLACEMENT_IMAGE_TAG);
|
||||
|
||||
@Test
|
||||
public void testDaprPlacementContainerDefaults() {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
|
||||
import io.dapr.client.DaprClientBuilder;
|
||||
import io.dapr.client.DaprClientImpl;
|
||||
import io.dapr.client.DaprPreviewClient;
|
||||
import io.dapr.config.Properties;
|
||||
import io.dapr.config.Property;
|
||||
import io.dapr.serializer.DefaultObjectSerializer;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class TestDaprJobsConfiguration {
|
||||
@Bean
|
||||
public DaprPreviewClient daprPreviewClient(
|
||||
@Value("${dapr.http.endpoint}") String daprHttpEndpoint,
|
||||
@Value("${dapr.grpc.endpoint}") String daprGrpcEndpoint
|
||||
){
|
||||
Map<Property<?>, String> overrides = Map.of(
|
||||
Properties.HTTP_ENDPOINT, daprHttpEndpoint,
|
||||
Properties.GRPC_ENDPOINT, daprGrpcEndpoint
|
||||
);
|
||||
|
||||
return new DaprClientBuilder().withPropertyOverrides(overrides).buildPreviewClient();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2024 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class TestJobsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestJobsApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -14,6 +14,7 @@ limitations under the License.
|
|||
package io.dapr.client;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Empty;
|
||||
import io.dapr.client.domain.ActorMetadata;
|
||||
|
@ -27,17 +28,21 @@ import io.dapr.client.domain.CloudEvent;
|
|||
import io.dapr.client.domain.ComponentMetadata;
|
||||
import io.dapr.client.domain.ConfigurationItem;
|
||||
import io.dapr.client.domain.DaprMetadata;
|
||||
import io.dapr.client.domain.DeleteJobRequest;
|
||||
import io.dapr.client.domain.DeleteStateRequest;
|
||||
import io.dapr.client.domain.ExecuteStateTransactionRequest;
|
||||
import io.dapr.client.domain.GetBulkSecretRequest;
|
||||
import io.dapr.client.domain.GetBulkStateRequest;
|
||||
import io.dapr.client.domain.GetConfigurationRequest;
|
||||
import io.dapr.client.domain.GetJobRequest;
|
||||
import io.dapr.client.domain.GetJobResponse;
|
||||
import io.dapr.client.domain.GetSecretRequest;
|
||||
import io.dapr.client.domain.GetStateRequest;
|
||||
import io.dapr.client.domain.HttpEndpointMetadata;
|
||||
import io.dapr.client.domain.HttpExtension;
|
||||
import io.dapr.client.domain.InvokeBindingRequest;
|
||||
import io.dapr.client.domain.InvokeMethodRequest;
|
||||
import io.dapr.client.domain.JobSchedule;
|
||||
import io.dapr.client.domain.LockRequest;
|
||||
import io.dapr.client.domain.PublishEventRequest;
|
||||
import io.dapr.client.domain.QueryStateItem;
|
||||
|
@ -45,6 +50,7 @@ import io.dapr.client.domain.QueryStateRequest;
|
|||
import io.dapr.client.domain.QueryStateResponse;
|
||||
import io.dapr.client.domain.RuleMetadata;
|
||||
import io.dapr.client.domain.SaveStateRequest;
|
||||
import io.dapr.client.domain.ScheduleJobRequest;
|
||||
import io.dapr.client.domain.State;
|
||||
import io.dapr.client.domain.StateOptions;
|
||||
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
||||
|
@ -90,9 +96,12 @@ import reactor.util.context.ContextView;
|
|||
import reactor.util.retry.Retry;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -1290,6 +1299,147 @@ public class DaprClientImpl extends AbstractDaprClient {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Mono<Void> scheduleJob(ScheduleJobRequest scheduleJobRequest) {
|
||||
try {
|
||||
validateScheduleJobRequest(scheduleJobRequest);
|
||||
|
||||
DaprProtos.Job.Builder scheduleJobRequestBuilder = DaprProtos.Job.newBuilder();
|
||||
scheduleJobRequestBuilder.setName(scheduleJobRequest.getName());
|
||||
|
||||
DateTimeFormatter iso8601Formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
.withZone(ZoneOffset.UTC);
|
||||
|
||||
if (scheduleJobRequest.getData() != null) {
|
||||
scheduleJobRequestBuilder.setData(Any.newBuilder()
|
||||
.setValue(ByteString.copyFrom(scheduleJobRequest.getData())).build());
|
||||
}
|
||||
|
||||
if (scheduleJobRequest.getSchedule() != null) {
|
||||
scheduleJobRequestBuilder.setSchedule(scheduleJobRequest.getSchedule().getExpression());
|
||||
}
|
||||
|
||||
if (scheduleJobRequest.getTtl() != null) {
|
||||
scheduleJobRequestBuilder.setTtl(iso8601Formatter.format(scheduleJobRequest.getTtl()));
|
||||
}
|
||||
|
||||
if (scheduleJobRequest.getRepeats() != null) {
|
||||
scheduleJobRequestBuilder.setRepeats(scheduleJobRequest.getRepeats());
|
||||
}
|
||||
|
||||
if (scheduleJobRequest.getDueTime() != null) {
|
||||
scheduleJobRequestBuilder.setDueTime(iso8601Formatter.format(scheduleJobRequest.getDueTime()));
|
||||
}
|
||||
|
||||
Mono<DaprProtos.ScheduleJobResponse> scheduleJobResponseMono =
|
||||
Mono.deferContextual(context -> this.createMono(
|
||||
it -> intercept(context, asyncStub)
|
||||
.scheduleJobAlpha1(DaprProtos.ScheduleJobRequest.newBuilder()
|
||||
.setJob(scheduleJobRequestBuilder.build()).build(), it)
|
||||
)
|
||||
);
|
||||
|
||||
return scheduleJobResponseMono.then();
|
||||
} catch (Exception ex) {
|
||||
return DaprException.wrapMono(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Mono<GetJobResponse> getJob(GetJobRequest getJobRequest) {
|
||||
try {
|
||||
validateGetJobRequest(getJobRequest);
|
||||
|
||||
Mono<DaprProtos.GetJobResponse> getJobResponseMono =
|
||||
Mono.deferContextual(context -> this.createMono(
|
||||
it -> intercept(context, asyncStub)
|
||||
.getJobAlpha1(DaprProtos.GetJobRequest.newBuilder()
|
||||
.setName(getJobRequest.getName()).build(), it)
|
||||
)
|
||||
);
|
||||
|
||||
return getJobResponseMono.map(response -> {
|
||||
DaprProtos.Job job = response.getJob();
|
||||
GetJobResponse getJobResponse = null;
|
||||
|
||||
if (job.hasSchedule() && job.hasDueTime()) {
|
||||
getJobResponse = new GetJobResponse(job.getName(), JobSchedule.fromString(job.getSchedule()));
|
||||
getJobResponse.setDueTime(Instant.parse(job.getDueTime()));
|
||||
} else if (job.hasSchedule()) {
|
||||
getJobResponse = new GetJobResponse(job.getName(), JobSchedule.fromString(job.getSchedule()));
|
||||
} else {
|
||||
getJobResponse = new GetJobResponse(job.getName(), Instant.parse(job.getDueTime()));
|
||||
}
|
||||
|
||||
return getJobResponse
|
||||
.setTtl(job.hasTtl() ? Instant.parse(job.getTtl()) : null)
|
||||
.setData(job.hasData() ? job.getData().getValue().toByteArray() : null)
|
||||
.setRepeat(job.hasRepeats() ? job.getRepeats() : null);
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
return DaprException.wrapMono(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Mono<Void> deleteJob(DeleteJobRequest deleteJobRequest) {
|
||||
try {
|
||||
validateDeleteJobRequest(deleteJobRequest);
|
||||
|
||||
Mono<DaprProtos.DeleteJobResponse> deleteJobResponseMono =
|
||||
Mono.deferContextual(context -> this.createMono(
|
||||
it -> intercept(context, asyncStub)
|
||||
.deleteJobAlpha1(DaprProtos.DeleteJobRequest.newBuilder()
|
||||
.setName(deleteJobRequest.getName()).build(), it)
|
||||
)
|
||||
);
|
||||
|
||||
return deleteJobResponseMono.then();
|
||||
} catch (Exception ex) {
|
||||
return DaprException.wrapMono(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateScheduleJobRequest(ScheduleJobRequest scheduleJobRequest) {
|
||||
if (scheduleJobRequest == null) {
|
||||
throw new IllegalArgumentException("scheduleJobRequest cannot be null");
|
||||
}
|
||||
|
||||
if (scheduleJobRequest.getName() == null || scheduleJobRequest.getName().isEmpty()) {
|
||||
throw new IllegalArgumentException("Name in the request cannot be null or empty");
|
||||
}
|
||||
|
||||
if (scheduleJobRequest.getSchedule() == null && scheduleJobRequest.getDueTime() == null) {
|
||||
throw new IllegalArgumentException("At least one of schedule or dueTime must be provided");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGetJobRequest(GetJobRequest getJobRequest) {
|
||||
if (getJobRequest == null) {
|
||||
throw new IllegalArgumentException("getJobRequest cannot be null");
|
||||
}
|
||||
|
||||
if (getJobRequest.getName() == null || getJobRequest.getName().isEmpty()) {
|
||||
throw new IllegalArgumentException("Name in the request cannot be null or empty");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDeleteJobRequest(DeleteJobRequest deleteJobRequest) {
|
||||
if (deleteJobRequest == null) {
|
||||
throw new IllegalArgumentException("deleteJobRequest cannot be null");
|
||||
}
|
||||
|
||||
if (deleteJobRequest.getName() == null || deleteJobRequest.getName().isEmpty()) {
|
||||
throw new IllegalArgumentException("Name in the request cannot be null or empty");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new Configuration Item from provided parameter.
|
||||
*
|
||||
|
@ -1494,5 +1644,4 @@ public class DaprClientImpl extends AbstractDaprClient {
|
|||
return new AppConnectionPropertiesHealthMetadata(healthCheckPath, healthProbeInterval, healthProbeTimeout,
|
||||
healthThreshold);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,9 +17,13 @@ import io.dapr.client.domain.BulkPublishEntry;
|
|||
import io.dapr.client.domain.BulkPublishRequest;
|
||||
import io.dapr.client.domain.BulkPublishResponse;
|
||||
import io.dapr.client.domain.BulkPublishResponseFailedEntry;
|
||||
import io.dapr.client.domain.DeleteJobRequest;
|
||||
import io.dapr.client.domain.GetJobRequest;
|
||||
import io.dapr.client.domain.GetJobResponse;
|
||||
import io.dapr.client.domain.LockRequest;
|
||||
import io.dapr.client.domain.QueryStateRequest;
|
||||
import io.dapr.client.domain.QueryStateResponse;
|
||||
import io.dapr.client.domain.ScheduleJobRequest;
|
||||
import io.dapr.client.domain.UnlockRequest;
|
||||
import io.dapr.client.domain.UnlockResponseStatus;
|
||||
import io.dapr.client.domain.query.Query;
|
||||
|
@ -268,4 +272,36 @@ public interface DaprPreviewClient extends AutoCloseable {
|
|||
*/
|
||||
<T> Subscription subscribeToEvents(
|
||||
String pubsubName, String topic, SubscriptionListener<T> listener, TypeRef<T> type);
|
||||
|
||||
/**
|
||||
* Schedules a job using the provided job request details.
|
||||
*
|
||||
* @param scheduleJobRequest The request containing the details of the job to schedule.
|
||||
* Must include a name and optional schedule, data, and other related properties.
|
||||
* @return A {@link Mono} that completes when the job scheduling operation is successful or raises an error.
|
||||
* @throws IllegalArgumentException If the request or its required fields like name are null or empty.
|
||||
*/
|
||||
public Mono<Void> scheduleJob(ScheduleJobRequest scheduleJobRequest);
|
||||
|
||||
/**
|
||||
* Retrieves details of a specific job.
|
||||
*
|
||||
* @param getJobRequest The request containing the job name for which the details are to be fetched.
|
||||
* The name property is mandatory.
|
||||
* @return A {@link Mono} that emits the {@link GetJobResponse} containing job details or raises an
|
||||
* error if the job is not found.
|
||||
* @throws IllegalArgumentException If the request or its required fields like name are null or empty.
|
||||
*/
|
||||
|
||||
public Mono<GetJobResponse> getJob(GetJobRequest getJobRequest);
|
||||
|
||||
/**
|
||||
* Deletes a job based on the given request.
|
||||
*
|
||||
* @param deleteJobRequest The request containing the job name to be deleted.
|
||||
* The name property is mandatory.
|
||||
* @return A {@link Mono} that completes when the job is successfully deleted or raises an error.
|
||||
* @throws IllegalArgumentException If the request or its required fields like name are null or empty.
|
||||
*/
|
||||
public Mono<Void> deleteJob(DeleteJobRequest deleteJobRequest);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.client.domain;
|
||||
|
||||
/**
|
||||
* Represents a request to schedule a job in Dapr.
|
||||
*/
|
||||
public class DeleteJobRequest {
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Constructor to create Delete Job Request.
|
||||
*
|
||||
* @param name of the job to delete.
|
||||
*/
|
||||
public DeleteJobRequest(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the job.
|
||||
*
|
||||
* @return The job name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.client.domain;
|
||||
|
||||
/**
|
||||
* Represents a request to schedule a job in Dapr.
|
||||
*/
|
||||
public class GetJobRequest {
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Constructor to create Get Job Request.
|
||||
*
|
||||
* @param name of the job to fetch..
|
||||
*/
|
||||
public GetJobRequest(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the job.
|
||||
*
|
||||
* @return The job name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.client.domain;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* Represents a request to schedule a job in Dapr.
|
||||
*/
|
||||
public class GetJobResponse {
|
||||
private final String name;
|
||||
private byte[] data;
|
||||
private JobSchedule schedule;
|
||||
private Instant dueTime;
|
||||
private Integer repeats;
|
||||
private Instant ttl;
|
||||
|
||||
/**
|
||||
* Constructor to create GetJobResponse.
|
||||
*
|
||||
* @param name of the job.
|
||||
* @param schedule job has to run.
|
||||
*/
|
||||
public GetJobResponse(String name, JobSchedule schedule) {
|
||||
this.name = name;
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to create GetJobResponse.
|
||||
*
|
||||
* @param name of the job.
|
||||
* @param dueTime An optional time at which the job should be active, or the “one shot” time, if other scheduling
|
||||
* type fields are not provided. Accepts a “point in time” string in the format of RFC3339,
|
||||
* Go duration string (calculated from creation time), or non-repeating ISO8601
|
||||
*/
|
||||
public GetJobResponse(String name, Instant dueTime) {
|
||||
this.name = name;
|
||||
this.dueTime = dueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data payload for the job.
|
||||
* This should be a JSON serialized value or object.
|
||||
*
|
||||
* @param data The job data in byte array format.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public GetJobResponse setData(byte[] data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the schedule for the job.
|
||||
* The format should follow cron expressions or other supported scheduling formats.
|
||||
*
|
||||
* @param schedule The job schedule.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public GetJobResponse setSchedule(JobSchedule schedule) {
|
||||
this.schedule = schedule;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the due time when the job should become active or execute once.
|
||||
* This can be in RFC3339, Go duration string, or non-repeating ISO8601 format.
|
||||
*
|
||||
* @param dueTime The due time of the job.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public GetJobResponse setDueTime(Instant dueTime) {
|
||||
this.dueTime = dueTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of times the job should be triggered.
|
||||
* If not set, the job runs indefinitely or until expiration.
|
||||
*
|
||||
* @param repeats The number of times the job should repeat.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public GetJobResponse setRepeat(Integer repeats) {
|
||||
this.repeats = repeats;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time-to-live (TTL) or expiration for the job.
|
||||
* This can be in RFC3339, Go duration string, or non-repeating ISO8601 format.
|
||||
*
|
||||
* @param ttl The time-to-live for the job.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public GetJobResponse setTtl(Instant ttl) {
|
||||
this.ttl = ttl;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
/**
|
||||
* Gets the name of the job.
|
||||
*
|
||||
* @return The job name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data payload of the job.
|
||||
*
|
||||
* @return The job data as a byte array.
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the schedule of the job.
|
||||
*
|
||||
* @return The job schedule.
|
||||
*/
|
||||
public JobSchedule getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the due time of the job.
|
||||
*
|
||||
* @return The due time.
|
||||
*/
|
||||
public Instant getDueTime() {
|
||||
return dueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of times the job should repeat.
|
||||
*
|
||||
* @return The repeat count, or null if not set.
|
||||
*/
|
||||
public Integer getRepeats() {
|
||||
return repeats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time-to-live (TTL) or expiration of the job.
|
||||
*
|
||||
* @return The TTL value.
|
||||
*/
|
||||
public Instant getTtl() {
|
||||
return ttl;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.client.domain;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Represents a job schedule using cron expressions or fixed intervals.
|
||||
* This class provides various static methods to create schedules based on predefined periods
|
||||
* (e.g., daily, weekly, monthly) or using custom cron expressions.
|
||||
* Example usage:
|
||||
* <pre>
|
||||
* JobsSchedule schedule = JobsSchedule.daily();
|
||||
* System.out.println(schedule.getExpression()); // Outputs: "0 0 0 * * *"
|
||||
* </pre>
|
||||
*/
|
||||
public class JobSchedule {
|
||||
|
||||
private final String expression;
|
||||
|
||||
/**
|
||||
* Private constructor to create a job schedule from a cron expression.
|
||||
*
|
||||
* @param expression the cron expression defining the schedule.
|
||||
*/
|
||||
private JobSchedule(String expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a job schedule from a fixed period using a {@link Duration}.
|
||||
* The resulting expression follows the format: "@every XhYmZsWms"
|
||||
* where X, Y, Z, and W represent hours, minutes, seconds, and milliseconds respectively.
|
||||
* Example:
|
||||
* <pre>
|
||||
* JobsSchedule schedule = JobsSchedule.fromPeriod(Duration.ofMinutes(30));
|
||||
* System.out.println(schedule.getExpression()); // Outputs: "@every 0h30m0s0ms"
|
||||
* </pre>
|
||||
*
|
||||
* @param duration the duration of the period.
|
||||
* @return a {@code JobsSchedule} with the corresponding interval.
|
||||
* @throws IllegalArgumentException if the duration is null.
|
||||
*/
|
||||
public static JobSchedule fromPeriod(Duration duration) {
|
||||
if (duration == null) {
|
||||
throw new IllegalArgumentException("duration cannot be null");
|
||||
}
|
||||
|
||||
String formattedDuration = String.format("%dh%dm%ds%dms",
|
||||
duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(), duration.toMillisPart());
|
||||
return new JobSchedule("@every " + formattedDuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a job schedule from a custom cron expression.
|
||||
*
|
||||
* @param cronExpression the cron expression.
|
||||
* @return a {@code JobsSchedule} representing the given cron expression.
|
||||
*/
|
||||
public static JobSchedule fromString(String cronExpression) {
|
||||
if (cronExpression == null) {
|
||||
throw new IllegalArgumentException("cronExpression cannot be null");
|
||||
}
|
||||
|
||||
return new JobSchedule(cronExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a yearly job schedule, running at midnight on January 1st.
|
||||
*
|
||||
* @return a {@code JobsSchedule} for yearly execution.
|
||||
*/
|
||||
public static JobSchedule yearly() {
|
||||
return new JobSchedule("0 0 0 1 1 *");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a monthly job schedule, running at midnight on the first day of each month.
|
||||
*
|
||||
* @return a {@code JobsSchedule} for monthly execution.
|
||||
*/
|
||||
public static JobSchedule monthly() {
|
||||
return new JobSchedule("0 0 0 1 * *");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a weekly job schedule, running at midnight on Sunday.
|
||||
*
|
||||
* @return a {@code JobsSchedule} for weekly execution.
|
||||
*/
|
||||
public static JobSchedule weekly() {
|
||||
return new JobSchedule("0 0 0 * * 0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a daily job schedule, running at midnight every day.
|
||||
*
|
||||
* @return a {@code JobsSchedule} for daily execution.
|
||||
*/
|
||||
public static JobSchedule daily() {
|
||||
return new JobSchedule("0 0 0 * * *");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an hourly job schedule, running at the start of every hour.
|
||||
*
|
||||
* @return a {@code JobsSchedule} for hourly execution.
|
||||
*/
|
||||
public static JobSchedule hourly() {
|
||||
return new JobSchedule("0 0 * * * *");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cron expression representing this job schedule.
|
||||
*
|
||||
* @return the cron expression as a string.
|
||||
*/
|
||||
public String getExpression() {
|
||||
return this.expression;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.client.domain;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* Represents a request to schedule a job in Dapr.
|
||||
*/
|
||||
public class ScheduleJobRequest {
|
||||
private final String name;
|
||||
private byte[] data;
|
||||
private JobSchedule schedule;
|
||||
private Instant dueTime;
|
||||
private Integer repeats;
|
||||
private Instant ttl;
|
||||
|
||||
/**
|
||||
* Constructor to create ScheduleJobRequest.
|
||||
*
|
||||
* @param name of the job.
|
||||
* @param schedule job has to run.
|
||||
*/
|
||||
public ScheduleJobRequest(String name, JobSchedule schedule) {
|
||||
this.name = name;
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to create ScheduleJobRequest.
|
||||
*
|
||||
* @param name of the job.
|
||||
* @param dueTime An optional time at which the job should be active, or the “one shot” time, if other scheduling
|
||||
* type fields are not provided. Accepts a “point in time” string in the format of RFC3339,
|
||||
* Go duration string (calculated from creation time), or non-repeating ISO8601
|
||||
*/
|
||||
public ScheduleJobRequest(String name, Instant dueTime) {
|
||||
this.name = name;
|
||||
this.dueTime = dueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data payload for the job.
|
||||
* This should be a JSON serialized value or object.
|
||||
*
|
||||
* @param data The job data in byte array format.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public ScheduleJobRequest setData(byte[] data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the schedule for the job.
|
||||
* The format should follow cron expressions or other supported scheduling formats.
|
||||
*
|
||||
* @param schedule The job schedule.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public ScheduleJobRequest setSchedule(JobSchedule schedule) {
|
||||
this.schedule = schedule;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the due time when the job should become active or execute once.
|
||||
* This can be in RFC3339, Go duration string, or non-repeating ISO8601 format.
|
||||
*
|
||||
* @param dueTime The due time of the job.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public ScheduleJobRequest setDueTime(Instant dueTime) {
|
||||
this.dueTime = dueTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of times the job should be triggered.
|
||||
* If not set, the job runs indefinitely or until expiration.
|
||||
*
|
||||
* @param repeats The number of times the job should repeat.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public ScheduleJobRequest setRepeat(Integer repeats) {
|
||||
this.repeats = repeats;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time-to-live (TTL) or expiration for the job.
|
||||
* This can be in RFC3339, Go duration string, or non-repeating ISO8601 format.
|
||||
*
|
||||
* @param ttl The time-to-live for the job.
|
||||
* @return This builder instance.
|
||||
*/
|
||||
public ScheduleJobRequest setTtl(Instant ttl) {
|
||||
this.ttl = ttl;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
/**
|
||||
* Gets the name of the job.
|
||||
*
|
||||
* @return The job name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data payload of the job.
|
||||
*
|
||||
* @return The job data as a byte array.
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the schedule of the job.
|
||||
*
|
||||
* @return The job schedule.
|
||||
*/
|
||||
public JobSchedule getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the due time of the job.
|
||||
*
|
||||
* @return The due time.
|
||||
*/
|
||||
public Instant getDueTime() {
|
||||
return dueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of times the job should repeat.
|
||||
*
|
||||
* @return The repeat count, or null if not set.
|
||||
*/
|
||||
public Integer getRepeats() {
|
||||
return repeats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time-to-live (TTL) or expiration of the job.
|
||||
*
|
||||
* @return The TTL value.
|
||||
*/
|
||||
public Instant getTtl() {
|
||||
return ttl;
|
||||
}
|
||||
}
|
|
@ -632,5 +632,4 @@ public class DaprClientHttpTest {
|
|||
daprClientHttp = buildDaprClient(daprHttp);
|
||||
daprClientHttp.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.client.domain;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class JobsScheduleTest {
|
||||
|
||||
@Test
|
||||
void testFromPeriodValidDuration() {
|
||||
Duration duration = Duration.ofHours(1).plusMinutes(30)
|
||||
.plusSeconds(15).plusMillis(500);
|
||||
JobSchedule schedule = JobSchedule.fromPeriod(duration);
|
||||
assertEquals("@every 1h30m15s500ms", schedule.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromPeriodValidDurationWithoutSecondsAndMillSeconds() {
|
||||
Duration duration = Duration.ofHours(1).plusMinutes(30);
|
||||
JobSchedule schedule = JobSchedule.fromPeriod(duration);
|
||||
assertEquals("@every 1h30m0s0ms", schedule.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromPeriodNullDuration() {
|
||||
Exception exception = assertThrows(IllegalArgumentException.class, () -> JobSchedule.fromPeriod(null));
|
||||
assertEquals("duration cannot be null", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromStringThrowsIllegalArgumentWhenExpressionIsNull() {
|
||||
Exception exception = assertThrows(IllegalArgumentException.class, () -> JobSchedule.fromString(null));
|
||||
assertEquals("cronExpression cannot be null", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromString() {
|
||||
String cronExpression = "0 0 * * *";
|
||||
JobSchedule schedule = JobSchedule.fromString(cronExpression);
|
||||
assertEquals(cronExpression, schedule.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testYearly() {
|
||||
JobSchedule schedule = JobSchedule.yearly();
|
||||
assertEquals("0 0 0 1 1 *", schedule.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMonthly() {
|
||||
JobSchedule schedule = JobSchedule.monthly();
|
||||
assertEquals("0 0 0 1 * *", schedule.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWeekly() {
|
||||
JobSchedule schedule = JobSchedule.weekly();
|
||||
assertEquals("0 0 0 * * 0", schedule.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDaily() {
|
||||
JobSchedule schedule = JobSchedule.daily();
|
||||
assertEquals("0 0 0 * * *", schedule.getExpression());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHourly() {
|
||||
JobSchedule schedule = JobSchedule.hourly();
|
||||
assertEquals("0 0 * * * *", schedule.getExpression());
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.testcontainers.containers.wait.strategy.Wait;
|
|||
import org.testcontainers.containers.wait.strategy.WaitStrategy;
|
||||
import org.testcontainers.images.builder.Transferable;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
import org.testcontainers.utility.MountableFile;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -62,14 +63,18 @@ public class DaprContainer extends GenericContainer<DaprContainer> {
|
|||
private DaprLogLevel daprLogLevel = DaprLogLevel.INFO;
|
||||
private String appChannelAddress = "localhost";
|
||||
private String placementService = "placement";
|
||||
private String schedulerService = "scheduler";
|
||||
private String placementDockerImageName = "daprio/placement";
|
||||
private String schedulerDockerImageName = "daprio/scheduler";
|
||||
|
||||
private Configuration configuration;
|
||||
private DaprPlacementContainer placementContainer;
|
||||
private DaprSchedulerContainer schedulerContainer;
|
||||
private String appName;
|
||||
private Integer appPort;
|
||||
private String appHealthCheckPath;
|
||||
private boolean shouldReusePlacement;
|
||||
private boolean shouldReuseScheduler;
|
||||
|
||||
/**
|
||||
* Creates a new Dapr container.
|
||||
|
@ -157,8 +162,13 @@ public class DaprContainer extends GenericContainer<DaprContainer> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public DaprContainer withReusablePlacement(boolean reuse) {
|
||||
this.shouldReusePlacement = reuse;
|
||||
public DaprContainer withReusablePlacement(boolean shouldReusePlacement) {
|
||||
this.shouldReusePlacement = shouldReusePlacement;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DaprContainer withReuseScheduler(boolean shouldReuseScheduler) {
|
||||
this.shouldReuseScheduler = shouldReuseScheduler;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -167,6 +177,11 @@ public class DaprContainer extends GenericContainer<DaprContainer> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public DaprContainer withSchedulerContainer(DaprSchedulerContainer schedulerContainer) {
|
||||
this.schedulerContainer = schedulerContainer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DaprContainer withComponent(Component component) {
|
||||
components.add(component);
|
||||
return this;
|
||||
|
@ -237,6 +252,14 @@ public class DaprContainer extends GenericContainer<DaprContainer> {
|
|||
this.placementContainer.start();
|
||||
}
|
||||
|
||||
if (this.schedulerContainer == null) {
|
||||
this.schedulerContainer = new DaprSchedulerContainer(this.schedulerDockerImageName)
|
||||
.withNetwork(getNetwork())
|
||||
.withNetworkAliases(schedulerService)
|
||||
.withReuse(this.shouldReuseScheduler);
|
||||
this.schedulerContainer.start();
|
||||
}
|
||||
|
||||
List<String> cmds = new ArrayList<>();
|
||||
cmds.add("./daprd");
|
||||
cmds.add("--app-id");
|
||||
|
@ -246,6 +269,8 @@ public class DaprContainer extends GenericContainer<DaprContainer> {
|
|||
cmds.add(DAPR_PROTOCOL.getName());
|
||||
cmds.add("--placement-host-address");
|
||||
cmds.add(placementService + ":50005");
|
||||
cmds.add("--scheduler-host-address");
|
||||
cmds.add(schedulerService + ":51005");
|
||||
|
||||
if (appChannelAddress != null && !appChannelAddress.isEmpty()) {
|
||||
cmds.add("--app-channel-address");
|
||||
|
@ -324,7 +349,7 @@ public class DaprContainer extends GenericContainer<DaprContainer> {
|
|||
withCopyToContainer(Transferable.of(endpointYaml), "/dapr-resources/" + endpoint.getName() + ".yaml");
|
||||
}
|
||||
|
||||
dependsOn(placementContainer);
|
||||
dependsOn(placementContainer, schedulerContainer);
|
||||
}
|
||||
|
||||
public String getAppName() {
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2024 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.testcontainers;
|
||||
|
||||
import org.testcontainers.containers.BindMode;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.images.builder.Transferable;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
import org.testcontainers.utility.MountableFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Test container for Dapr scheduler service.
|
||||
*/
|
||||
public class DaprSchedulerContainer extends GenericContainer<DaprSchedulerContainer> {
|
||||
|
||||
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("daprio/scheduler");
|
||||
private int schedulerPort = 51005;
|
||||
|
||||
/**
|
||||
* Creates a new Dapr scheduler container.
|
||||
* @param dockerImageName Docker image name.
|
||||
*/
|
||||
public DaprSchedulerContainer(DockerImageName dockerImageName) {
|
||||
super(dockerImageName);
|
||||
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
|
||||
withExposedPorts(schedulerPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Dapr scheduler container.
|
||||
* @param image Docker image name.
|
||||
*/
|
||||
public DaprSchedulerContainer(String image) {
|
||||
this(DockerImageName.parse(image));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
|
||||
withCopyToContainer(Transferable.of("", 0777), "./default-dapr-scheduler-server-0/dapr-0.1/");
|
||||
withCopyToContainer(Transferable.of("", 0777), "./dapr-scheduler-existing-cluster/");
|
||||
withCommand("./scheduler", "--port", Integer.toString(schedulerPort), "--etcd-data-dir", ".");
|
||||
}
|
||||
|
||||
public static DockerImageName getDefaultImageName() {
|
||||
return DEFAULT_IMAGE_NAME;
|
||||
}
|
||||
|
||||
public DaprSchedulerContainer withPort(Integer port) {
|
||||
this.schedulerPort = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return schedulerPort;
|
||||
}
|
||||
|
||||
// Required by spotbugs plugin
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return super.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue