Skip to content

Commit

Permalink
Comprehensive Spring Boot examples (#349)
Browse files Browse the repository at this point in the history
* Start spring-examples

* Fixing TODOs for TaskDescriptor

* LongRunning task example

* MultiInstance recurring task example

* StateTracking recurring task example

* Correct copyright for google code

* Restructuring

* Cleaning up. Examples in README

* Cleaning
  • Loading branch information
kagkarlsson authored Mar 27, 2023
1 parent 9611008 commit cc67408
Show file tree
Hide file tree
Showing 46 changed files with 1,889 additions and 71 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ scheduler.schedule(myAdhocTask.instance("1045", new MyTaskData(1001L)), Instant.

### More examples

#### Plain Java

* [EnableImmediateExecutionMain.java](./examples/features/src/main/java/com/github/kagkarlsson/examples/EnableImmediateExecutionMain.java)
* [MaxRetriesMain.java](./examples/features/src/main/java/com/github/kagkarlsson/examples/MaxRetriesMain.java)
* [ExponentialBackoffMain.java](./examples/features/src/main/java/com/github/kagkarlsson/examples/ExponentialBackoffMain.java)
Expand All @@ -159,6 +161,21 @@ scheduler.schedule(myAdhocTask.instance("1045", new MyTaskData(1001L)), Instant.
* [JobChainingUsingTaskDataMain.java](./examples/features/src/main/java/com/github/kagkarlsson/examples/JobChainingUsingTaskDataMain.java)
* [JobChainingUsingSeparateTasksMain.java](./examples/features/src/main/java/com/github/kagkarlsson/examples/JobChainingUsingSeparateTasksMain.java)

#### Spring Boot



| Example | Description |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [BasicExamples](./examples/spring-boot-example/src/main/java/com/github/kagkarlsson/examples/boot/config/BasicExamplesConfiguration.java) | A basic one-time task and recurring task |
| [TransactionallyStagedJob](./examples/spring-boot-example/src/main/java/com/github/kagkarlsson/examples/boot/config/TransactionallyStagedJobConfiguration.java) | Example of [transactionally staging a job](https://brandur.org/job-drain), i.e. making sure the background job runs **iff** the transaction commits (along with other db-modifications). |
| [LongRunningJob](./examples/spring-boot-example/src/main/java/com/github/kagkarlsson/examples/boot/config/LongRunningJobConfiguration.java) | Long-running jobs need to **survive application restarts** and avoid restarting from the beginning. This example demonstrates how to **persisting progress** on shutdown and additionally a technique for limiting the job to run nightly. |
| [RecurringStateTracking](./examples/spring-boot-example/src/main/java/com/github/kagkarlsson/examples/boot/config/RecurringStateTrackingConfiguration.java) | A recurring task with state that can be modified after each run. |
| [ParallellJobSpawner](./examples/spring-boot-example/src/main/java/com/github/kagkarlsson/examples/boot/config/ParallellJobConfiguration.java) | Demonstrates how to use a recurring job to spawn one-time jobs, e.g. for parallelization. |
| [JobChaining](./examples/spring-boot-example/src/main/java/com/github/kagkarlsson/examples/boot/config/JobChainingConfiguration.java) | A one-time job with **multiple steps**. The next step is scheduled after the previous one completes. |
| [MultiInstanceRecurring](./examples/spring-boot-example/src/main/java/com/github/kagkarlsson/examples/boot/config/MultiInstanceRecurringConfiguration.java) | Demonstrates how to achieve **multiple recurring jobs** of the same type, but potentially differing schedules and data. |



## Configuration

Expand Down
5 changes: 5 additions & 0 deletions db-scheduler/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
<artifactId>jackson-annotations</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<optional>true</optional>
</dependency>


<!-- Test -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ public Instant getExecutionTime() {

@SuppressWarnings("unchecked")
public DATA_TYPE getData() {
if (dataClass.isInstance(this.execution.taskInstance.getData())) {
return (DATA_TYPE) this.execution.taskInstance.getData();
Object data = this.execution.taskInstance.getData();
if (data == null) {
return null;
} else if (dataClass.isInstance(data)) {
return (DATA_TYPE) data;
}
throw new DataClassMismatchException(dataClass, this.execution.taskInstance.getData().getClass());
throw new DataClassMismatchException(dataClass, data.getClass());
}

public Instant getLastSuccess() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,40 @@
*/
package com.github.kagkarlsson.scheduler.serializer;

import com.github.kagkarlsson.scheduler.serializer.gson.InstantAdapter;
import com.github.kagkarlsson.scheduler.serializer.gson.*;
import com.github.kagkarlsson.scheduler.task.schedule.CronSchedule;
import com.github.kagkarlsson.scheduler.task.schedule.Daily;
import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay;
import com.github.kagkarlsson.scheduler.task.schedule.Schedule;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.function.Consumer;

public class GsonSerializer implements Serializer {
public static final Charset CHARSET = StandardCharsets.UTF_8;
private final Gson gson;

public static GsonBuilder getDefaultGson() {
RuntimeTypeAdapterFactory<Schedule> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Schedule.class, "type")
.registerSubtype(CronSchedule.class, "cron")
.registerSubtype(FixedDelay.class, "fixedDelay")
.registerSubtype(Daily.class, "daily");

return new GsonBuilder()
.serializeNulls()
.registerTypeAdapter(Instant.class, new InstantAdapter());
.registerTypeAdapter(Instant.class, new InstantAdapter())
.registerTypeAdapter(Duration.class, new DurationAdapter())
.registerTypeAdapter(LocalTime.class, new LocalTimeAdapter())
.registerTypeHierarchyAdapter(ZoneId.class, new ZoneIdAdapter())
.registerTypeAdapterFactory(runtimeTypeAdapterFactory);
}

public GsonSerializer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,17 @@
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.github.kagkarlsson.scheduler.exceptions.SerializationException;
import com.github.kagkarlsson.scheduler.serializer.gson.InstantAdapter;
import com.github.kagkarlsson.scheduler.serializer.jackson.ScheduleMixin;
import com.github.kagkarlsson.scheduler.serializer.jackson.InstantDeserializer;
import com.github.kagkarlsson.scheduler.serializer.jackson.InstantSerializer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.github.kagkarlsson.scheduler.task.helper.ScheduleAndData;
import com.github.kagkarlsson.scheduler.task.schedule.Schedule;

import java.io.IOException;
import java.sql.Time;
import java.time.Instant;
import java.util.function.Consumer;

Expand All @@ -46,7 +44,9 @@ public static ObjectMapper getDefaultObjectMapper() {
return new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
.registerModule(module);
.addMixIn(Schedule.class, ScheduleMixin.class)
.registerModule(module)
.registerModule(new JavaTimeModule());
}

public JacksonSerializer() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (C) Gustav Karlsson
*
* 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 com.github.kagkarlsson.scheduler.serializer.gson;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeFormatter;

public class DurationAdapter extends TypeAdapter<Duration> {

@Override
public void write(JsonWriter jsonWriter, Duration duration) throws IOException {
jsonWriter.value(duration.toMillis());
}

@Override
public Duration read(JsonReader jsonReader) throws IOException {
return Duration.ofMillis(Long.parseLong(jsonReader.nextString()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (C) Gustav Karlsson
*
* 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 com.github.kagkarlsson.scheduler.serializer.gson;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;

public class LocalTimeAdapter extends TypeAdapter<LocalTime> {

private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);

@Override
public void write(JsonWriter jsonWriter, LocalTime localTime) throws IOException {
jsonWriter.value(localTime.format(FORMATTER));
}

@Override
public LocalTime read(JsonReader jsonReader) throws IOException {
return FORMATTER.parse(jsonReader.nextString(), LocalTime::from);
}
}
Loading

0 comments on commit cc67408

Please sign in to comment.