Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.dbos.transact.exceptions;

/**
* {@code DbosSerializableException} is used to serialize the exception when the original one is not
* serializable.
*/
public class DbosSerializableException extends RuntimeException {
private final String originalClassName;

public DbosSerializableException(Throwable throwable) {
super(throwable.getMessage());
originalClassName = throwable.getClass().getName();
setStackTrace(throwable.getStackTrace());
}

public String getOriginalClassName() {
return originalClassName;
}
}
30 changes: 20 additions & 10 deletions transact/src/main/java/dev/dbos/transact/json/JSONUtil.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
package dev.dbos.transact.json;

import dev.dbos.transact.conductor.Conductor;
import dev.dbos.transact.exceptions.DbosSerializableException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.io.UncheckedIOException;
import java.io.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
Expand Down Expand Up @@ -127,12 +122,27 @@ public static WireThrowable toWire(Throwable t, Map<String, Object> extra, Strin
w.node = node;
w.extra = (extra == null) ? Map.of() : extra;

byte[] javaSer = javaSerialize(t);
String b64 = Base64.getEncoder().encodeToString(javaSer);
w.base64bytes = b64;
w.base64bytes = serializeThrowable(t);
return w;
}

private static String serializeThrowable(Throwable t) {
try {
byte[] javaSer;
try {
javaSer = javaSerialize(t);
} catch (Exception e) {
logger.warn("Failed to serialize Throwable", e);
RuntimeException o = new DbosSerializableException(t);
javaSer = javaSerialize(o);
}
return Base64.getEncoder().encodeToString(javaSer);
} catch (Exception e) {
logger.error("Failed to serialize Throwable", e);
return null;
}
}

public static Throwable toThrowable(WireThrowable w, ClassLoader loader)
throws IOException, ClassNotFoundException {
if (w.base64bytes == null) throw new IllegalArgumentException("No serialized payload");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,69 @@ public void workflowWithError() throws Exception {
assertEquals(WorkflowState.ERROR.name(), handle.getStatus().status());
}

@Test
public void workWithNonSerializableException() throws SQLException {
SimpleService simpleService =
DBOS.registerWorkflows(SimpleService.class, new SimpleServiceImpl());

DBOS.launch();

String wfid = "abc";
WorkflowHandle<Void, ?> handle =
DBOS.startWorkflow(
() -> {
simpleService.workWithNonSerializableException();
return null;
},
new StartWorkflowOptions(wfid));

var e = assertThrows(Exception.class, () -> handle.getResult());
assertEquals("workNonSerializableException error", e.getMessage());

List<WorkflowStatus> wfs = DBOS.listWorkflows(new ListWorkflowsInput());
assertEquals(1, wfs.size());
assertEquals(wfs.get(0).name(), "workNonSerializableException");
assertNotNull(wfs.get(0).workflowId());
assertEquals(wfs.get(0).workflowId(), handle.workflowId());
assertEquals(
"dev.dbos.transact.workflow.SimpleServiceImpl$NonSerializableException",
wfs.get(0).error().className());
assertEquals("workNonSerializableException error", wfs.get(0).error().message());
assertNotNull(wfs.get(0).workflowId());
}

@Test
public void workWithNonSerializableExceptionInStep() throws SQLException {
SimpleService simpleService =
DBOS.registerWorkflows(SimpleService.class, new SimpleServiceImpl());
simpleService.setSimpleService(simpleService);

DBOS.launch();

String wfid = "abc";
WorkflowHandle<Void, ?> handle =
DBOS.startWorkflow(
() -> {
simpleService.workWithNonSerializableExceptionInStep();
return null;
},
new StartWorkflowOptions(wfid));

var e = assertThrows(Exception.class, () -> handle.getResult());
assertEquals("stepWithNonSerializableException error", e.getMessage());

List<WorkflowStatus> wfs = DBOS.listWorkflows(new ListWorkflowsInput());
assertEquals(1, wfs.size());
assertEquals(wfs.get(0).name(), "workWithNonSerializableExceptionInStep");
assertNotNull(wfs.get(0).workflowId());
assertEquals(wfs.get(0).workflowId(), handle.workflowId());
assertEquals(
"dev.dbos.transact.workflow.SimpleServiceImpl$NonSerializableException",
wfs.get(0).error().className());
assertEquals("stepWithNonSerializableException error", wfs.get(0).error().message());
assertNotNull(wfs.get(0).workflowId());
}

@Test
public void childWorkflowWithoutSet() throws Exception {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ public interface SimpleService {

public void workWithError() throws Exception;

public void workWithNonSerializableException();

public void workWithNonSerializableExceptionInStep();

public String parentWorkflowWithoutSet(String input);

public String workflowWithMultipleChildren(String input) throws Exception;
Expand All @@ -30,6 +34,8 @@ public interface SimpleService {

void stepWithSleep(long sleepSeconds);

void stepWithNonSerializableException();

String longParent(String input, long sleepSeconds, long timeoutSeconds)
throws InterruptedException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ public void workWithError() throws Exception {
throw new Exception("DBOS Test error");
}

@Workflow(name = "workNonSerializableException")
public void workWithNonSerializableException() {
throw new NonSerializableException("workNonSerializableException error");
}

@Workflow(name = "workWithNonSerializableExceptionInStep")
public void workWithNonSerializableExceptionInStep() {
simpleService.stepWithNonSerializableException();
}

@Workflow(name = "parentWorkflowWithoutSet")
public String parentWorkflowWithoutSet(String input) {
String result = input;
Expand Down Expand Up @@ -141,6 +151,11 @@ public void stepWithSleep(long sleepSeconds) {
}
}

@Step(name = "stepWithNonSerializableException")
public void stepWithNonSerializableException() {
throw new NonSerializableException("stepWithNonSerializableException error");
}

@Workflow(name = "childWorkflowWithSleep")
public String childWorkflowWithSleep(String input, long sleepSeconds)
throws InterruptedException {
Expand Down Expand Up @@ -210,4 +225,14 @@ public void startWfInStepById(String childId) {
},
"startWfInStepById");
}

private static class NonSerializableException extends RuntimeException {
private NonSerializableClass nonSerializableValue = new NonSerializableClass();

public NonSerializableException(String message) {
super(message);
}

private static class NonSerializableClass {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,48 @@ public void workflowWithError() throws SQLException {
assertNotNull(wfs.get(0).workflowId());
}

@Test
public void workWithNonSerializableException() throws SQLException {
SimpleService simpleService =
DBOS.registerWorkflows(SimpleService.class, new SimpleServiceImpl());

DBOS.launch();

var e = assertThrows(Exception.class, () -> simpleService.workWithNonSerializableException());
assertEquals("workNonSerializableException error", e.getMessage());

List<WorkflowStatus> wfs = DBOS.listWorkflows(new ListWorkflowsInput());
assertEquals(1, wfs.size());
assertEquals(wfs.get(0).name(), "workNonSerializableException");
assertEquals(
"dev.dbos.transact.workflow.SimpleServiceImpl$NonSerializableException",
wfs.get(0).error().className());
assertEquals("workNonSerializableException error", wfs.get(0).error().message());
assertNotNull(wfs.get(0).workflowId());
}

@Test
public void workWithNonSerializableExceptionInStep() throws SQLException {
SimpleService simpleService =
DBOS.registerWorkflows(SimpleService.class, new SimpleServiceImpl());
simpleService.setSimpleService(simpleService);

DBOS.launch();

var e =
assertThrows(Exception.class, () -> simpleService.workWithNonSerializableExceptionInStep());
assertEquals("stepWithNonSerializableException error", e.getMessage());

List<WorkflowStatus> wfs = DBOS.listWorkflows(new ListWorkflowsInput());
assertEquals(1, wfs.size());
assertEquals(wfs.get(0).name(), "workWithNonSerializableExceptionInStep");
assertEquals(
"dev.dbos.transact.workflow.SimpleServiceImpl$NonSerializableException",
wfs.get(0).error().className());
assertEquals("stepWithNonSerializableException error", wfs.get(0).error().message());
assertNotNull(wfs.get(0).workflowId());
}

@Test
public void setWorkflowId() throws SQLException {

Expand Down