This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit b36450f394af14a2b97f3dc847778707d9528535
Author: Rene Cordier <[email protected]>
AuthorDate: Fri Apr 3 16:40:51 2026 +0700

    JAMES-4197 EventSerializer refactoring
    
    The goal is to have methods return Optional, so the 
EventSerializersAggregator stops relying on json (de)serialization failures 
until finding the right serializer.
---
 .../org/apache/james/events/EventSerializer.java   | 21 ++++----
 .../james/events/EventSerializersAggregator.java   | 60 +++++-----------------
 .../apache/james/events/EventBusTestFixture.java   | 37 +++++++------
 .../james/events/CassandraEventDeadLettersDAO.java |  8 ++-
 .../org/apache/james/events/EventDispatcher.java   | 10 +++-
 .../apache/james/events/GroupConsumerRetry.java    |  3 +-
 .../org/apache/james/events/GroupRegistration.java |  4 +-
 .../james/events/GroupRegistrationHandler.java     |  4 +-
 .../james/events/KeyRegistrationHandler.java       | 16 +++---
 .../apache/james/events/RabbitMQEventBusTest.java  |  2 +-
 .../james/events/PostgresEventDeadLetters.java     |  9 +++-
 .../apache/james/json/JsonGenericSerializer.java   | 42 ++++++++++++---
 .../james/event/json/MailboxEventSerializer.scala  | 28 +++++++---
 .../james/event/json/AddedSerializationTest.java   |  6 +--
 .../event/json/ExpungedSerializationTest.java      |  6 +--
 .../event/json/FlagsUpdatedSerializationTest.java  |  6 +--
 .../MailboxACLUpdatedEventSerializationTest.java   |  2 +-
 .../event/json/MailboxAddedSerializationTest.java  |  2 +-
 .../json/MailboxDeletionSerializationTest.java     |  4 +-
 .../json/MailboxRenamedSerializationTest.java      |  2 +-
 .../MessageContentDeletionSerializationTest.java   |  6 +--
 .../json/MessageMoveEventSerializationTest.java    |  8 +--
 .../QuotaUsageUpdatedEventSerializationTest.java   |  2 +-
 .../james/jmap/change/JmapEventSerializer.scala    | 15 +++---
 .../webadmin/routes/EventDeadLettersRoutes.java    |  6 ++-
 25 files changed, 174 insertions(+), 135 deletions(-)

diff --git 
a/event-bus/api/src/main/java/org/apache/james/events/EventSerializer.java 
b/event-bus/api/src/main/java/org/apache/james/events/EventSerializer.java
index 72360b861a..c3bf6b3869 100644
--- a/event-bus/api/src/main/java/org/apache/james/events/EventSerializer.java
+++ b/event-bus/api/src/main/java/org/apache/james/events/EventSerializer.java
@@ -22,29 +22,30 @@ package org.apache.james.events;
 import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 
 public interface EventSerializer {
-    String toJson(Event event);
+    Optional<String> toJson(Event event);
 
-    String toJson(Collection<Event> event);
+    Optional<String> toJson(Collection<Event> event);
 
-    default byte[] toJsonBytes(Event event) {
-        return toJson(event).getBytes(StandardCharsets.UTF_8);
+    default Optional<byte[]> toJsonBytes(Event event) {
+        return toJson(event).map(json -> 
json.getBytes(StandardCharsets.UTF_8));
     }
 
-    default byte[] toJsonBytes(Collection<Event> event) {
-        return toJson(event).getBytes(StandardCharsets.UTF_8);
+    default Optional<byte[]> toJsonBytes(Collection<Event> event) {
+        return toJson(event).map(json -> 
json.getBytes(StandardCharsets.UTF_8));
     }
 
-    Event asEvent(String serialized);
+    Optional<Event> asEvent(String serialized);
 
-    List<Event> asEvents(String serialized);
+    Optional<List<Event>> asEvents(String serialized);
 
-    default Event fromBytes(byte[] serialized) {
+    default Optional<Event> fromBytes(byte[] serialized) {
         return asEvent(new String(serialized, StandardCharsets.UTF_8));
     }
 
-    default List<Event> asEventsFromBytes(byte[] serialized) {
+    default Optional<List<Event>> asEventsFromBytes(byte[] serialized) {
         return asEvents(new String(serialized, StandardCharsets.UTF_8));
     }
 }
diff --git 
a/event-bus/api/src/main/java/org/apache/james/events/EventSerializersAggregator.java
 
b/event-bus/api/src/main/java/org/apache/james/events/EventSerializersAggregator.java
index 0661de6c00..86b8baf3f0 100644
--- 
a/event-bus/api/src/main/java/org/apache/james/events/EventSerializersAggregator.java
+++ 
b/event-bus/api/src/main/java/org/apache/james/events/EventSerializersAggregator.java
@@ -37,70 +37,34 @@ public class EventSerializersAggregator implements 
EventSerializer {
     }
 
     @Override
-    public String toJson(Event event) {
+    public Optional<String> toJson(Event event) {
         return allEventSerializers.stream()
-            .map(eventSerializer -> serialize(event, eventSerializer))
+            .map(eventSerializer -> eventSerializer.toJson(event))
             .flatMap(Optional::stream)
-            .findFirst()
-            .orElseThrow(() -> new RuntimeException("Could not serialize 
event: " + event));
+            .findFirst();
     }
 
     @Override
-    public Event asEvent(String serialized) {
+    public Optional<Event> asEvent(String serialized) {
         return allEventSerializers.stream()
-            .map(eventSerializer -> deserialize(serialized, eventSerializer))
+            .map(eventSerializer -> eventSerializer.asEvent(serialized))
             .flatMap(Optional::stream)
-            .findFirst()
-            .orElseThrow(() -> new RuntimeException("Could not deserialize 
event: " + serialized));
+            .findFirst();
     }
 
     @Override
-    public String toJson(Collection<Event> events) {
+    public Optional<String> toJson(Collection<Event> events) {
         return allEventSerializers.stream()
-            .map(eventSerializer -> serialize(events, eventSerializer))
+            .map(eventSerializer -> eventSerializer.toJson(events))
             .flatMap(Optional::stream)
-            .findFirst()
-            .orElseThrow(() -> new RuntimeException("Could not serialize 
event: " + events));
+            .findFirst();
     }
 
     @Override
-    public List<Event> asEvents(String serialized) {
+    public Optional<List<Event>> asEvents(String serialized) {
         return allEventSerializers.stream()
-            .map(eventSerializer -> deserializeEvents(serialized, 
eventSerializer))
+            .map(eventSerializer -> eventSerializer.asEvents(serialized))
             .flatMap(Optional::stream)
-            .findFirst()
-            .orElseThrow(() -> new RuntimeException("Could not deserialize 
event: " + serialized));
-    }
-
-    private Optional<String> serialize(Event event, EventSerializer 
eventSerializer) {
-        try {
-            return Optional.of(eventSerializer.toJson(event));
-        } catch (Exception ex) {
-            return Optional.empty();
-        }
-    }
-
-    private Optional<String> serialize(Collection<Event> event, 
EventSerializer eventSerializer) {
-        try {
-            return Optional.of(eventSerializer.toJson(event));
-        } catch (Exception ex) {
-            return Optional.empty();
-        }
-    }
-
-    private Optional<Event> deserialize(String json, EventSerializer 
eventSerializer) {
-        try {
-            return Optional.of(eventSerializer.asEvent(json));
-        } catch (Exception ex) {
-            return Optional.empty();
-        }
-    }
-
-    private Optional<List<Event>> deserializeEvents(String json, 
EventSerializer eventSerializer) {
-        try {
-            return Optional.of(eventSerializer.asEvents(json));
-        } catch (Exception ex) {
-            return Optional.empty();
-        }
+            .findFirst();
     }
 }
diff --git 
a/event-bus/api/src/test/java/org/apache/james/events/EventBusTestFixture.java 
b/event-bus/api/src/test/java/org/apache/james/events/EventBusTestFixture.java
index cf1fb30def..b56985cfc9 100644
--- 
a/event-bus/api/src/test/java/org/apache/james/events/EventBusTestFixture.java
+++ 
b/event-bus/api/src/test/java/org/apache/james/events/EventBusTestFixture.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
 import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
@@ -34,7 +35,6 @@ import java.util.stream.Collectors;
 import org.apache.james.core.Username;
 
 import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -178,36 +178,43 @@ public interface EventBusTestFixture {
         static final String ARRAY_SEPARATOR = "^";
 
         @Override
-        public String toJson(Event event) {
-            Preconditions.checkArgument(event instanceof TestEvent || event 
instanceof UnsupportedEvent);
-            return event.getClass().getCanonicalName() + "&" + 
event.getEventId().getId().toString() + "&" + event.getUsername().asString();
+        public Optional<String> toJson(Event event) {
+            if (!(event instanceof TestEvent || event instanceof 
UnsupportedEvent)) {
+                return Optional.empty();
+            }
+            return Optional.of(event.getClass().getCanonicalName() + "&" + 
event.getEventId().getId().toString() + "&" + event.getUsername().asString());
         }
 
         @Override
-        public Event asEvent(String serialized) {
-            Preconditions.checkArgument(serialized.contains("&"));
-            Preconditions.checkArgument(!serialized.contains(ARRAY_SEPARATOR));
+        public Optional<Event> asEvent(String serialized) {
+            if (!serialized.contains("&") || 
serialized.contains(ARRAY_SEPARATOR)) {
+                return Optional.empty();
+            }
             List<String> parts = Splitter.on("&").splitToList(serialized);
-            
Preconditions.checkArgument(parts.get(0).equals(TestEvent.class.getCanonicalName()));
+            if (!parts.get(0).equals(TestEvent.class.getCanonicalName())) {
+                return Optional.empty();
+            }
 
             Event.EventId eventId = 
Event.EventId.of(UUID.fromString(parts.get(1)));
             Username username = 
Username.of(Joiner.on("&").join(parts.stream().skip(2).collect(ImmutableList.toImmutableList())));
-            return new TestEvent(eventId, username);
+            return Optional.of(new TestEvent(eventId, username));
         }
 
         @Override
-        public String toJson(Collection<Event> event) {
-            return event.stream()
+        public Optional<String> toJson(Collection<Event> event) {
+            return Optional.of(event.stream()
                 .map(this::toJson)
-                .collect(Collectors.joining(ARRAY_SEPARATOR));
+                .map(Optional::get)
+                .collect(Collectors.joining(ARRAY_SEPARATOR)));
         }
 
         @Override
-        public List<Event> asEvents(String serialized) {
-            return Splitter.on(ARRAY_SEPARATOR)
+        public Optional<List<Event>> asEvents(String serialized) {
+            return Optional.of(Splitter.on(ARRAY_SEPARATOR)
                 .splitToStream(serialized)
                 .map(this::asEvent)
-                .collect(ImmutableList.toImmutableList());
+                .map(Optional::get)
+                .collect(ImmutableList.toImmutableList()));
         }
     }
 
diff --git 
a/event-bus/cassandra/src/main/java/org/apache/james/events/CassandraEventDeadLettersDAO.java
 
b/event-bus/cassandra/src/main/java/org/apache/james/events/CassandraEventDeadLettersDAO.java
index 4014caa13d..d7a547c643 100644
--- 
a/event-bus/cassandra/src/main/java/org/apache/james/events/CassandraEventDeadLettersDAO.java
+++ 
b/event-bus/cassandra/src/main/java/org/apache/james/events/CassandraEventDeadLettersDAO.java
@@ -104,10 +104,13 @@ public class CassandraEventDeadLettersDAO {
     }
 
     Mono<Void> store(Group group, Event failedEvent, 
EventDeadLetters.InsertionId insertionId) {
+        String serializedEvent = eventSerializer.toJson(failedEvent)
+            .orElseThrow(() -> new RuntimeException("Could not serialize 
event: " + failedEvent));
+
         return executor.executeVoid(insertStatement.bind()
                 .setString(GROUP, group.asString())
                 .setUuid(INSERTION_ID, insertionId.getId())
-                .setString(EVENT, eventSerializer.toJson(failedEvent)));
+                .setString(EVENT, serializedEvent));
     }
 
     Mono<Void> removeEvent(Group group, EventDeadLetters.InsertionId 
failedInsertionId) {
@@ -139,6 +142,7 @@ public class CassandraEventDeadLettersDAO {
     }
 
     private Event deserializeEvent(String serializedEvent) {
-        return eventSerializer.asEvent(serializedEvent);
+        return eventSerializer.asEvent(serializedEvent)
+            .orElseThrow(() -> new RuntimeException("Could not deserialize 
event: " + serializedEvent));
     }
 }
diff --git 
a/event-bus/distributed/src/main/java/org/apache/james/events/EventDispatcher.java
 
b/event-bus/distributed/src/main/java/org/apache/james/events/EventDispatcher.java
index f0316ea3e2..ef74ebce34 100644
--- 
a/event-bus/distributed/src/main/java/org/apache/james/events/EventDispatcher.java
+++ 
b/event-bus/distributed/src/main/java/org/apache/james/events/EventDispatcher.java
@@ -180,7 +180,7 @@ public class EventDispatcher {
             .flatMap(event -> event.keys().stream())
             .collect(ImmutableSet.toImmutableSet());
 
-        return Mono.fromCallable(() -> 
eventSerializer.toJsonBytes(underlyingEvents))
+        return Mono.fromCallable(() -> serializeEvents(underlyingEvents))
             .flatMap(serializedEvent -> Mono.zipDelayError(
                 remoteGroupsDispatch(serializedEvent, underlyingEvents),
                 remoteKeysDispatch(serializedEvent, keys)))
@@ -258,6 +258,12 @@ public class EventDispatcher {
     }
 
     private byte[] serializeEvent(Event event) {
-        return eventSerializer.toJsonBytes(event);
+        return eventSerializer.toJsonBytes(event)
+            .orElseThrow(() -> new RuntimeException("Could not serialize 
event: " + event));
+    }
+
+    private byte[] serializeEvents(Collection<Event> event) {
+        return eventSerializer.toJsonBytes(event)
+            .orElseThrow(() -> new RuntimeException("Could not serialize 
events"));
     }
 }
diff --git 
a/event-bus/distributed/src/main/java/org/apache/james/events/GroupConsumerRetry.java
 
b/event-bus/distributed/src/main/java/org/apache/james/events/GroupConsumerRetry.java
index 545fbe3fe3..9b03c9171d 100644
--- 
a/event-bus/distributed/src/main/java/org/apache/james/events/GroupConsumerRetry.java
+++ 
b/event-bus/distributed/src/main/java/org/apache/james/events/GroupConsumerRetry.java
@@ -100,7 +100,8 @@ class GroupConsumerRetry {
     }
 
     private Mono<Void> sendRetryMessage(Event event, int currentRetryCount) {
-        byte[] eventAsBytes = eventSerializer.toJsonBytes(event);
+        byte[] eventAsBytes = eventSerializer.toJsonBytes(event)
+            .orElseThrow(() -> new RuntimeException("Could not serialize 
event: " + event));
 
         Mono<OutboundMessage> retryMessage = Mono.just(new OutboundMessage(
             retryExchangeName.asString(),
diff --git 
a/event-bus/distributed/src/main/java/org/apache/james/events/GroupRegistration.java
 
b/event-bus/distributed/src/main/java/org/apache/james/events/GroupRegistration.java
index bd9e72f4a9..a3be6d6eeb 100644
--- 
a/event-bus/distributed/src/main/java/org/apache/james/events/GroupRegistration.java
+++ 
b/event-bus/distributed/src/main/java/org/apache/james/events/GroupRegistration.java
@@ -27,6 +27,7 @@ import static 
org.apache.james.backends.rabbitmq.Constants.evaluateAutoDelete;
 import static org.apache.james.backends.rabbitmq.Constants.evaluateDurable;
 import static org.apache.james.backends.rabbitmq.Constants.evaluateExclusive;
 
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -177,7 +178,8 @@ class GroupRegistration implements Registration {
     }
 
     private Mono<Event> deserializeEvent(byte[] eventAsBytes) {
-        return Mono.fromCallable(() -> eventSerializer.fromBytes(eventAsBytes))
+        return Mono.fromCallable(() -> eventSerializer.fromBytes(eventAsBytes)
+                .orElseThrow(() -> new RuntimeException("Could not deserialize 
event: " + new String(eventAsBytes, StandardCharsets.UTF_8))))
             .subscribeOn(Schedulers.parallel());
     }
 
diff --git 
a/event-bus/distributed/src/main/java/org/apache/james/events/GroupRegistrationHandler.java
 
b/event-bus/distributed/src/main/java/org/apache/james/events/GroupRegistrationHandler.java
index 6f4b540f53..69e1add06a 100644
--- 
a/event-bus/distributed/src/main/java/org/apache/james/events/GroupRegistrationHandler.java
+++ 
b/event-bus/distributed/src/main/java/org/apache/james/events/GroupRegistrationHandler.java
@@ -152,7 +152,9 @@ public class GroupRegistrationHandler {
     }
 
     private Mono<List<Event>> deserializeEvents(byte[] eventAsBytes) {
-        return Mono.fromCallable(() -> 
eventSerializer.asEventsFromBytes(eventAsBytes));
+        return Mono.fromCallable(() ->
+            eventSerializer.asEventsFromBytes(eventAsBytes)
+                .orElseThrow(() -> new RuntimeException("Could not deserialize 
events")));
     }
 
     void stop() {
diff --git 
a/event-bus/distributed/src/main/java/org/apache/james/events/KeyRegistrationHandler.java
 
b/event-bus/distributed/src/main/java/org/apache/james/events/KeyRegistrationHandler.java
index ab378fbec6..3a9c371678 100644
--- 
a/event-bus/distributed/src/main/java/org/apache/james/events/KeyRegistrationHandler.java
+++ 
b/event-bus/distributed/src/main/java/org/apache/james/events/KeyRegistrationHandler.java
@@ -26,6 +26,7 @@ import static 
org.apache.james.backends.rabbitmq.Constants.evaluateDurable;
 import static org.apache.james.backends.rabbitmq.Constants.evaluateExclusive;
 import static org.apache.james.events.RabbitMQEventBus.EVENT_BUS_ID;
 
+import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.util.List;
 import java.util.Optional;
@@ -218,14 +219,12 @@ class KeyRegistrationHandler {
         byte[] bodyAsBytes = deliver.getBody();
         // if the json is an array, we have multiple events
         if (bodyAsBytes != null && bodyAsBytes.length > 0 && bodyAsBytes[0] == 
'[') {
-            return eventSerializer.asEventsFromBytes(bodyAsBytes);
+            return deserializeEvents(bodyAsBytes);
         }
 
-        try {
-            return List.of(eventSerializer.fromBytes(bodyAsBytes));
-        } catch (RuntimeException exception) {
-            return eventSerializer.asEventsFromBytes(bodyAsBytes);
-        }
+        return eventSerializer.fromBytes(bodyAsBytes)
+            .map(List::of)
+            .orElse(deserializeEvents(bodyAsBytes));
     }
 
     private StructuredLogger structuredLogger(List<Event> events, 
RegistrationKey key) {
@@ -241,4 +240,9 @@ class KeyRegistrationHandler {
                 .collect(Collectors.joining(",")))
             .field(EventBus.StructuredLoggingFields.REGISTRATION_KEY, 
key.asString());
     }
+
+    private List<Event> deserializeEvents(byte[] bodyAsBytes) {
+        return eventSerializer.asEventsFromBytes(bodyAsBytes)
+            .orElseThrow(() -> new RuntimeException("Could not deserialize 
events: " + new String(bodyAsBytes, StandardCharsets.UTF_8)));
+    }
 }
diff --git 
a/event-bus/distributed/src/test/java/org/apache/james/events/RabbitMQEventBusTest.java
 
b/event-bus/distributed/src/test/java/org/apache/james/events/RabbitMQEventBusTest.java
index e5addb75c6..38eb279be2 100644
--- 
a/event-bus/distributed/src/test/java/org/apache/james/events/RabbitMQEventBusTest.java
+++ 
b/event-bus/distributed/src/test/java/org/apache/james/events/RabbitMQEventBusTest.java
@@ -431,7 +431,7 @@ class RabbitMQEventBusTest implements 
GroupContract.SingleEventBusGroupContract,
                     .blockFirst()
                     .getBody();
 
-                return eventSerializer.asEvent(new String(eventInBytes, 
StandardCharsets.UTF_8));
+                return eventSerializer.asEvent(new String(eventInBytes, 
StandardCharsets.UTF_8)).get();
             }
         }
     }
diff --git 
a/event-bus/postgres/src/main/java/org/apache/james/events/PostgresEventDeadLetters.java
 
b/event-bus/postgres/src/main/java/org/apache/james/events/PostgresEventDeadLetters.java
index 39b79145ed..4c4bb07357 100644
--- 
a/event-bus/postgres/src/main/java/org/apache/james/events/PostgresEventDeadLetters.java
+++ 
b/event-bus/postgres/src/main/java/org/apache/james/events/PostgresEventDeadLetters.java
@@ -50,11 +50,14 @@ public class PostgresEventDeadLetters implements 
EventDeadLetters {
         Preconditions.checkArgument(registeredGroup != null, 
REGISTERED_GROUP_CANNOT_BE_NULL);
         Preconditions.checkArgument(failDeliveredEvent != null, 
FAIL_DELIVERED_EVENT_CANNOT_BE_NULL);
 
+        String serializedEvent = eventSerializer.toJson(failDeliveredEvent)
+            .orElseThrow(() -> new RuntimeException("Could not serialize 
event: " + failDeliveredEvent));
+
         InsertionId insertionId = InsertionId.random();
         return postgresExecutor.executeVoid(dslContext -> 
Mono.from(dslContext.insertInto(TABLE_NAME)
                 .set(INSERTION_ID, insertionId.getId())
                 .set(GROUP, registeredGroup.asString())
-                .set(EVENT, eventSerializer.toJson(failDeliveredEvent))))
+                .set(EVENT, serializedEvent)))
             .thenReturn(insertionId);
     }
 
@@ -87,7 +90,9 @@ public class PostgresEventDeadLetters implements 
EventDeadLetters {
     }
 
     private Event deserializeEvent(Record record) {
-        return eventSerializer.asEvent(record.get(EVENT));
+        String serializedEvent = record.get(EVENT);
+        return eventSerializer.asEvent(serializedEvent)
+            .orElseThrow(() -> new RuntimeException("Could not deserialize 
event: " + serializedEvent));
     }
 
     @Override
diff --git 
a/json/src/main/java/org/apache/james/json/JsonGenericSerializer.java 
b/json/src/main/java/org/apache/james/json/JsonGenericSerializer.java
index 4f4be5f2e9..d863e9bb3e 100644
--- a/json/src/main/java/org/apache/james/json/JsonGenericSerializer.java
+++ b/json/src/main/java/org/apache/james/json/JsonGenericSerializer.java
@@ -22,6 +22,7 @@ package org.apache.james.json;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -36,6 +37,7 @@ import com.fasterxml.jackson.databind.jsontype.NamedType;
 import com.fasterxml.jackson.datatype.guava.GuavaModule;
 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.github.fge.lambdas.Throwing;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
@@ -122,16 +124,31 @@ public class JsonGenericSerializer<T, U extends DTO> {
         return objectMapper.writeValueAsString(dto);
     }
 
-    public byte[] serializeToBytes(T domainObject) throws 
JsonProcessingException {
-        U dto = dtoConverter.toDTO(domainObject)
-            .orElseThrow(() -> new UnknownTypeException("unknown type " + 
domainObject.getClass()));
-        return objectMapper.writeValueAsBytes(dto);
+    public Optional<String> maybeSerialize(T domainObject) {
+        try {
+            return dtoConverter.toDTO(domainObject)
+                
.map(Throwing.function(objectMapper::writeValueAsString).sneakyThrow());
+        } catch (Exception exception) {
+            return Optional.empty();
+        }
     }
 
-    public T deserializeFromBytes(byte[] value) throws IOException {
-        U dto = jsonToDTO(value);
-        return dtoConverter.toDomainObject(dto)
-            .orElseThrow(() -> new UnknownTypeException("unknown type " + 
dto.getType()));
+    public Optional<byte[]> maybeSerializeToBytes(T domainObject) {
+        try {
+            return dtoConverter.toDTO(domainObject)
+                
.map(Throwing.function(objectMapper::writeValueAsBytes).sneakyThrow());
+        } catch (Exception exception) {
+            return Optional.empty();
+        }
+    }
+
+    public Optional<T> maybeDeserializeFromBytes(byte[] value) {
+        try {
+            U dto = jsonToDTO(value);
+            return dtoConverter.toDomainObject(dto);
+        } catch (Exception e) {
+            return Optional.empty();
+        }
     }
 
     public T deserialize(String value) throws IOException {
@@ -140,6 +157,15 @@ public class JsonGenericSerializer<T, U extends DTO> {
             .orElseThrow(() -> new UnknownTypeException("unknown type " + 
dto.getType()));
     }
 
+    public Optional<T> maybeDeserialize(String value) {
+        try {
+            U dto = jsonToDTO(value);
+            return dtoConverter.toDomainObject(dto);
+        } catch (Exception e) {
+            return Optional.empty();
+        }
+    }
+
     private U jsonToDTO(String value) throws IOException {
         try {
             JsonNode jsonTree = detectDuplicateProperty(value);
diff --git 
a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MailboxEventSerializer.scala
 
b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MailboxEventSerializer.scala
index 17b3a2008d..b0ddc58daa 100644
--- 
a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MailboxEventSerializer.scala
+++ 
b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MailboxEventSerializer.scala
@@ -21,7 +21,7 @@ package org.apache.james.event.json
 
 import java.time.Instant
 import java.util
-import java.util.{TreeMap => JavaTreeMap}
+import java.util.{Optional, TreeMap => JavaTreeMap}
 
 import jakarta.inject.Inject
 import julienrf.json.derived
@@ -456,17 +456,31 @@ class JsonSerialize(mailboxIdFactory: MailboxId.Factory, 
messageIdFactory: Messa
 class MailboxEventSerializer @Inject()(mailboxIdFactory: MailboxId.Factory, 
messageIdFactory: MessageId.Factory, quotaRootDeserializer: 
QuotaRootDeserializer) extends EventSerializer {
   private val jsonSerialize = new JsonSerialize(mailboxIdFactory, 
messageIdFactory, quotaRootDeserializer)
 
-  override def toJson(event: JavaEvent): String = jsonSerialize.toJson(event)
+  override def toJson(event: JavaEvent): Optional[String] = 
Optional.of(jsonSerialize.toJson(event))
 
-  override def toJsonBytes(event: JavaEvent): Array[Byte] = 
jsonSerialize.toJsonBytes(event)
+  override def toJsonBytes(event: JavaEvent): Optional[Array[Byte]] = 
Optional.of(jsonSerialize.toJsonBytes(event))
 
-  override def toJsonBytes(event: util.Collection[JavaEvent]): Array[Byte] = 
jsonSerialize.toJsonBytes(event)
+  override def toJsonBytes(event: util.Collection[JavaEvent]): 
Optional[Array[Byte]] = Optional.of(jsonSerialize.toJsonBytes(event))
 
   def fromJson(json: String): JsResult[JavaEvent] = 
jsonSerialize.fromJson(json)
 
-  override def toJson(event: util.Collection[JavaEvent]): String = 
jsonSerialize.toJson(event)
+  override def toJson(event: util.Collection[JavaEvent]): Optional[String] = 
Optional.of(jsonSerialize.toJson(event))
 
-  override def asEvents(serialized: String): util.List[JavaEvent] = 
jsonSerialize.fromJsonAsEvents(serialized).get.asJava
+  override def asEvents(serialized: String): Optional[util.List[JavaEvent]] = {
+    val result = jsonSerialize.fromJsonAsEvents(serialized)
+    if (result.isError) {
+      Optional.empty()
+    } else {
+      Optional.of(result.get.asJava)
+    }
+  }
 
-  override def asEvent(serialized: String): JavaEvent = 
fromJson(serialized).get
+  override def asEvent(serialized: String): Optional[JavaEvent] = {
+    val result = fromJson(serialized)
+    if (result.isError) {
+      Optional.empty()
+    } else {
+      Optional.of(result.get)
+    }
+  }
 }
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
index b82ff1dc12..d5eb72a30a 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
@@ -172,13 +172,13 @@ class AddedSerializationTest {
 
     @Test
     void addedShouldBeWellSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_ADDED_EVENT))
+        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_ADDED_EVENT).get())
             .isEqualTo(DEFAULT_ADDED_EVENT_JSON);
     }
 
     @Test
     void addedWithDistinctMessageIdAndThreadIdShouldBeWellSerialized() {
-        
assertThatJson(EVENT_SERIALIZER.toJson(ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT))
+        
assertThatJson(EVENT_SERIALIZER.toJson(ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT).get())
             
.isEqualTo(ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT_JSON);
     }
 
@@ -252,7 +252,7 @@ class AddedSerializationTest {
 
         @Test
         void addedShouldBeWellSerializedWhenMapKeyIsEmpty() {
-            assertThatJson(EVENT_SERIALIZER.toJson(emptyAddedEvent))
+            assertThatJson(EVENT_SERIALIZER.toJson(emptyAddedEvent).get())
                 .isEqualTo(emptyAddedEventJson);
         }
 
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
index 8dfb31a1d4..e9f4550322 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
@@ -166,13 +166,13 @@ class ExpungedSerializationTest {
 
     @Test
     void expungedShouldBeWellSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_EXPUNGED_EVENT))
+        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_EXPUNGED_EVENT).get())
             .isEqualTo(DEFAULT_EXPUNGED_EVENT_JSON);
     }
 
     @Test
     void expungedWithDistinctMessageIdAndThreadIdShouldBeWellSerialized() {
-        
assertThatJson(EVENT_SERIALIZER.toJson(EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT))
+        
assertThatJson(EVENT_SERIALIZER.toJson(EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT).get())
             
.isEqualTo(EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT_JSON);
     }
 
@@ -242,7 +242,7 @@ class ExpungedSerializationTest {
 
         @Test
         void expungedShouldBeWellSerializedWhenMapKeyIsEmpty() {
-            assertThatJson(EVENT_SERIALIZER.toJson(emptyExpungedEvent))
+            assertThatJson(EVENT_SERIALIZER.toJson(emptyExpungedEvent).get())
                 .isEqualTo(emptyExpungedEventJson);
         }
 
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
index 11fc5c184f..3ffa626bf5 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
@@ -129,7 +129,7 @@ class FlagsUpdatedSerializationTest {
 
     @Test
     void flagsUpdatedShouldBeWellSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_EVENT))
+        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_EVENT).get())
             .when(Option.IGNORING_ARRAY_ORDER)
             .isEqualTo(DEFAULT_EVENT_JSON);
     }
@@ -164,7 +164,7 @@ class FlagsUpdatedSerializationTest {
 
         @Test
         void flagsUpdatedShouldBeWellSerialized() {
-            assertThatJson(EVENT_SERIALIZER.toJson(emptyUpdatedFlagsEvent))
+            
assertThatJson(EVENT_SERIALIZER.toJson(emptyUpdatedFlagsEvent).get())
                 .when(Option.IGNORING_ARRAY_ORDER)
                 .isEqualTo(EVENT_JSON_WITH_EMPTY_UPDATED_FLAGS);
         }
@@ -272,7 +272,7 @@ class FlagsUpdatedSerializationTest {
 
         @Test
         void flagsUpdatedShouldBeWellSerialized() {
-            assertThatJson(EVENT_SERIALIZER.toJson(eventWithMessageIds))
+            assertThatJson(EVENT_SERIALIZER.toJson(eventWithMessageIds).get())
                 .when(Option.IGNORING_ARRAY_ORDER)
                 .isEqualTo(EVENT_WITH_MESSAGE_IDS_JSON);
         }
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
index d1de1dc5b4..405a596f1f 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
@@ -75,7 +75,7 @@ class MailboxACLUpdatedEventSerializationTest {
 
     @Test
     void mailboxACLUpdatedShouldBeSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(MAILBOX_ACL_UPDATED))
+        assertThatJson(EVENT_SERIALIZER.toJson(MAILBOX_ACL_UPDATED).get())
             .isEqualTo(MAILBOX_ACL_UPDATED_JSON);
     }
 
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
index 1063d2f021..d80f32b19f 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
@@ -60,7 +60,7 @@ class MailboxAddedSerializationTest {
 
     @Test
     void mailboxAddedShouldBeWellSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(EVENT_1))
+        assertThatJson(EVENT_SERIALIZER.toJson(EVENT_1).get())
             .isEqualTo(JSON_1);
     }
 
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java
index 99f70726d7..264959a83c 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java
@@ -139,7 +139,7 @@ class MailboxDeletionSerializationTest {
 
     @Test
     void mailboxDeletionShouldBeWellSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_MAILBOX_DELETION_EVENT))
+        
assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_MAILBOX_DELETION_EVENT).get())
             .isEqualTo(DEFAULT_EVENT_JSON);
     }
 
@@ -151,7 +151,7 @@ class MailboxDeletionSerializationTest {
 
     @Test
     void mailboxDeletionWithEmptyACLShouldBeWellSerialized() {
-        
assertThatJson(EVENT_SERIALIZER.toJson(EMPTY_ACL_MAILBOX_DELETION_EVENT))
+        
assertThatJson(EVENT_SERIALIZER.toJson(EMPTY_ACL_MAILBOX_DELETION_EVENT).get())
             .isEqualTo(EVENT_WITH_EMPTY_ACL_JSON);
     }
 
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java
index 18e55d41c8..9158755962 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java
@@ -76,7 +76,7 @@ class MailboxRenamedSerializationTest {
 
     @Test
     void mailboxRenamedShouldBeWellSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_MAILBOX_RENAMED_EVENT))
+        
assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_MAILBOX_RENAMED_EVENT).get())
             .isEqualTo(DEFAULT_MAILBOX_RENAMED_EVENT_JSON);
     }
 
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageContentDeletionSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageContentDeletionSerializationTest.java
index 39b663f8ac..23749b7760 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageContentDeletionSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageContentDeletionSerializationTest.java
@@ -234,8 +234,8 @@ class MessageContentDeletionSerializationTest {
 
     @Test
     void messageContentDeletionEventShouldBeWellSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(EVENT)).isEqualTo(JSON);
-        
assertThatJson(EVENT_SERIALIZER.toJson(EVENT_WITHOUT_HEADER_CONTENT)).isEqualTo(JSON_WITHOUT_HEADER_CONTENT);
+        assertThatJson(EVENT_SERIALIZER.toJson(EVENT).get()).isEqualTo(JSON);
+        
assertThatJson(EVENT_SERIALIZER.toJson(EVENT_WITHOUT_HEADER_CONTENT).get()).isEqualTo(JSON_WITHOUT_HEADER_CONTENT);
     }
 
     @Test
@@ -246,7 +246,7 @@ class MessageContentDeletionSerializationTest {
 
     @Test
     void messageContentDeletionEventWithMailboxPathShouldBeWellSerialized() {
-        
assertThatJson(EVENT_SERIALIZER.toJson(EVENT_WITH_MAILBOX_PATH)).isEqualTo(JSON_WITH_MAILBOX_PATH);
+        
assertThatJson(EVENT_SERIALIZER.toJson(EVENT_WITH_MAILBOX_PATH).get()).isEqualTo(JSON_WITH_MAILBOX_PATH);
     }
 
     @Test
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageMoveEventSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageMoveEventSerializationTest.java
index b24bc93049..9e2067a75f 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageMoveEventSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageMoveEventSerializationTest.java
@@ -59,7 +59,7 @@ class MessageMoveEventSerializationTest {
 
     @Test
     void messageMoveEventShouldBeWellSerialized() {
-        assertThatJson(EVENT_SERIALIZER.toJson(EVENT))
+        assertThatJson(EVENT_SERIALIZER.toJson(EVENT).get())
             .isEqualTo(JSON);
     }
 
@@ -94,7 +94,7 @@ class MessageMoveEventSerializationTest {
 
             @Test
             void messageMoveEventShouldBeWellSerialized() {
-                assertThatJson(EVENT_SERIALIZER.toJson(event))
+                assertThatJson(EVENT_SERIALIZER.toJson(event).get())
                     .isEqualTo(json);
             }
 
@@ -128,7 +128,7 @@ class MessageMoveEventSerializationTest {
 
             @Test
             void messageMoveEventShouldBeWellSerialized() {
-                assertThatJson(EVENT_SERIALIZER.toJson(event))
+                assertThatJson(EVENT_SERIALIZER.toJson(event).get())
                     .isEqualTo(json);
             }
 
@@ -162,7 +162,7 @@ class MessageMoveEventSerializationTest {
 
             @Test
             void messageMoveEventShouldBeWellSerialized() {
-                assertThatJson(EVENT_SERIALIZER.toJson(event))
+                assertThatJson(EVENT_SERIALIZER.toJson(event).get())
                     .isEqualTo(json);
             }
 
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java
index 7991349789..f1703a787e 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java
@@ -77,7 +77,7 @@ class QuotaUsageUpdatedEventSerializationTest {
 
     @Test
     void toJsonShouldReturnQuotaEventJson() {
-        assertThatJson(EVENT_SERIALIZER.toJson(eventWithUserContainsUsername))
+        
assertThatJson(EVENT_SERIALIZER.toJson(eventWithUserContainsUsername).get())
             .isEqualTo(quotaUsageUpdatedEvent);
     }
 
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/change/JmapEventSerializer.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/change/JmapEventSerializer.scala
index e614af67ac..08118e10f8 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/change/JmapEventSerializer.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/change/JmapEventSerializer.scala
@@ -85,20 +85,21 @@ case class JmapEventSerializer 
@Inject()(stateChangeEventDTOFactory: StateChange
     
.forModules(stateChangeEventDTOFactory.dtoModule.asInstanceOf[EventDTOModule[Event,
 EventDTO]])
     .withoutNestedType()
 
-  override def toJson(event: Event): String = 
genericSerializer.serialize(event)
+  override def toJson(event: Event): Optional[String] = 
genericSerializer.maybeSerialize(event)
 
-  override def asEvent(serialized: String): Event = 
genericSerializer.deserialize(serialized)
+  override def asEvent(serialized: String): Optional[Event] = 
genericSerializer.maybeDeserialize(serialized)
 
-  override def toJsonBytes(event: Event): Array[Byte] =  
genericSerializer.serializeToBytes(event)
+  override def toJsonBytes(event: Event): Optional[Array[Byte]] = 
genericSerializer.maybeSerializeToBytes(event)
 
-  override def fromBytes(serialized: Array[Byte]): Event = 
genericSerializer.deserializeFromBytes(serialized)
+  override def fromBytes(serialized: Array[Byte]): Optional[Event] = 
genericSerializer.maybeDeserializeFromBytes(serialized)
 
-  override def toJson(event: util.Collection[Event]): String = {
+  override def toJson(event: util.Collection[Event]): Optional[String] = {
     if (event.size() != 1) {
-      throw new IllegalArgumentException("Not supported for multiple events, 
please serialize separately")
+      Optional.empty()
     }
     toJson(event.iterator().next())
   }
 
-  override def asEvents(serialized: String): util.List[Event] = 
ImmutableList.of(asEvent(serialized))
+  override def asEvents(serialized: String): Optional[util.List[Event]] =
+    asEvent(serialized).map(event => ImmutableList.of(event))
 }
diff --git 
a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/EventDeadLettersRoutes.java
 
b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/EventDeadLettersRoutes.java
index 86e79a2f27..74a7751ca4 100644
--- 
a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/EventDeadLettersRoutes.java
+++ 
b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/EventDeadLettersRoutes.java
@@ -106,8 +106,10 @@ public class EventDeadLettersRoutes implements Routes {
         EventDeadLetters.InsertionId insertionId = parseInsertionId(request);
 
         return eventDeadLettersService.getEvent(group, insertionId)
-            .map(eventSerializer::toJson)
-            .switchIfEmpty(Mono.fromRunnable(() ->  
response.status(HttpStatus.NOT_FOUND_404)))
+            .flatMap(event -> eventSerializer.toJson(event)
+                .map(Mono::just)
+                .orElseGet(Mono::empty))
+            .switchIfEmpty(Mono.fromRunnable(() -> 
response.status(HttpStatus.NOT_FOUND_404)))
             .block();
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to