diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/task/PersistentTask.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/task/PersistentTask.java
index e62257eda..342c10163 100644
--- a/core/src/main/java/org/sterl/spring/persistent_tasks/api/task/PersistentTask.java
+++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/task/PersistentTask.java
@@ -66,6 +66,5 @@ default boolean isTransactional() {
* @param e the exception reason - could also be a {@link FailTaskNoRetryException}
* @see Failed trigger
*/
- default void afterTriggerFailed(@Nullable T state, Exception e) {
- }
+ default void afterTriggerFailed(@Nullable T state, Exception e) {}
}
diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/shared/converter/ToTrigger.java b/core/src/main/java/org/sterl/spring/persistent_tasks/shared/converter/ToTrigger.java
index fa2a5775b..541bc328c 100644
--- a/core/src/main/java/org/sterl/spring/persistent_tasks/shared/converter/ToTrigger.java
+++ b/core/src/main/java/org/sterl/spring/persistent_tasks/shared/converter/ToTrigger.java
@@ -7,6 +7,9 @@
import org.sterl.spring.persistent_tasks.shared.model.TriggerEntity;
import org.sterl.spring.persistent_tasks.trigger.component.StateSerializer;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
public enum ToTrigger implements ExtendetConvert {
INSTANCE;
@@ -29,8 +32,25 @@ public Trigger convert(@NonNull HasTrigger hasData) {
result.setRunAt(source.getRunAt());
result.setRunningDurationInMs(source.getRunningDurationInMs());
result.setStart(source.getStart());
- result.setState(SERIALIZER.deserialize(source.getState()));
+ try {
+ result.setState(SERIALIZER.deserialize(source.getState()));
+ } catch (Exception e) {
+ var info = """
+ Failed to deserialize state
+ This is most likely due to an incompatible code change.
+ Old states in the DB cannot be read anymore/deserialized and cast to the given class.
+ """;
+ result.setState(new FailedToReadStateInfo(e.getMessage(), info));
+ log.warn("""
+ Failed to deserialize state of {}.
+ This is most likely due to an incompatible code change.
+ Old states in the DB cannot be read anymore/deserialized and cast to the given class.
+ {}""",
+ source.getKey(), e.getMessage());
+ }
result.setStatus(source.getStatus());
return result;
}
+
+ record FailedToReadStateInfo(String message, String info) {}
}
\ No newline at end of file
diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java
index bd5f3dbb3..6ec08ba2f 100644
--- a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java
+++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java
@@ -77,9 +77,9 @@ public Page searchTriggers(@Nullable TriggerSearch search,
return result;
} else {
log.debug("Empty search={}, selecting all triggers for page={}", search, page);
- return triggerRepository.findAll(page);
+ var result = triggerRepository.findAll(page);
+ return result;
}
-
}
public Page searchGroupedTriggers(@Nullable TriggerSearch search, Pageable page) {
diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/StateSerializer.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/StateSerializer.java
index 410d70986..f2d7940fb 100644
--- a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/StateSerializer.java
+++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/StateSerializer.java
@@ -2,9 +2,11 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.ObjectInput;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
import java.io.Serializable;
import org.sterl.spring.persistent_tasks.exception.SpringPersistentTaskException;
@@ -50,7 +52,7 @@ public Serializable deserialize(byte[] bytes) {
}
var bis = new ByteArrayInputStream(bytes);
- try (ObjectInput in = new ObjectInputStream(bis)) {
+ try (var in = new ContextClassLoaderObjectInputStream(bis)) {
return (Serializable)in.readObject();
} catch (Exception ex) {
throw new DeSerializationFailedException(bytes, ex);
@@ -65,4 +67,18 @@ public Serializable deserializeOrNull(byte[] bytes) {
return null;
}
}
+
+ // needed for spring boot developer tools
+ // https://github.com/sterlp/spring-persistent-tasks/issues/19
+ static class ContextClassLoaderObjectInputStream extends ObjectInputStream {
+ ContextClassLoaderObjectInputStream(InputStream in) throws IOException {
+ super(in);
+ }
+ @Override
+ protected Class> resolveClass(ObjectStreamClass desc)
+ throws IOException, ClassNotFoundException {
+ return Class.forName(desc.getName(), false,
+ Thread.currentThread().getContextClassLoader());
+ }
+ }
}
diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java
index e4299f307..82e854b65 100644
--- a/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java
+++ b/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java
@@ -27,6 +27,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
+import com.github.f4b6a3.uuid.UuidCreator;
class TriggerResourceTest extends AbstractSpringTest {
@@ -217,6 +218,27 @@ void testGroupSearch() throws JsonMappingException, JsonProcessingException {
assertThat(result.getBody()).contains("\"groupByValue\":\"a1\"");
}
+ @Test
+ void testReadIncompatibleTriggerState() {
+ // GIVEN we have a bad state we cannot read in the DB
+ var key = UuidCreator.getTimeOrdered().toString();
+ trx.execute(t -> {
+ return triggerRepository.save(new RunningTriggerEntity(
+ new TriggerKey(key, "slowTask")
+ ).withState(new byte[] {12, 54, 33})
+ );
+ });
+
+ // WHEN
+ assertThat(triggerRepository.count()).isGreaterThanOrEqualTo(1L);
+ var response = template.getForEntity(baseUrl, String.class);
+
+ // THEN
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(response.getBody()).contains(key);
+ assertThat(response.getBody()).contains("Failed to deserialize state of length");
+ }
+
private RunningTriggerEntity createStatus(TriggerKey key, TriggerStatus status) {
final var now = OffsetDateTime.now();
final var isCancel = status == TriggerStatus.CANCELED;
diff --git a/example/pom.xml b/example/pom.xml
index 40a804def..11b756a74 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -17,7 +17,7 @@
- 2.2.3-SNAPSHOT
+ 2.2.4-SNAPSHOT
@@ -48,12 +48,6 @@
org.springframework.boot
spring-boot-starter-web
-
-
- org.springframework.data
- spring-data-envers
-
-
org.springframework.boot
@@ -69,7 +63,7 @@
org.springdoc
springdoc-openapi-starter-webmvc-ui
- 2.6.0
+ 2.8.13
@@ -98,5 +92,24 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
\ No newline at end of file