Skip to content

Commit 8485792

Browse files
committed
Relay project events as raw JSON instead of deserializing them
Delivering a change bundle failed at the gateway with a Jackson error: the sequenced event and the history-query response typed their inner events against this service's own event model, and the event-history service's serialization of a hierarchy-changed event does not construct under it. The gateway never acts on the inner events — it only relays them to browsers — so the event list and the sequenced bundle now carry them as raw JSON. What reaches the client is byte-for-byte what the producer serialized, and the gateway stops breaking whenever its copy of the event model drifts from a producer's. Found running the end-to-end suite against the epic stack: viewers opened streams fine but never received a single event. Part of protegeproject/webprotege-gwt-ui#304 (epic protegeproject/webprotege-gwt-ui#303).
1 parent 6a12751 commit 8485792

5 files changed

Lines changed: 22 additions & 12 deletions

File tree

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package edu.stanford.protege.webprotege.gateway.websocket.dto;
22

3-
import edu.stanford.protege.webprotege.common.Event;
3+
import com.fasterxml.jackson.databind.JsonNode;
44
import edu.stanford.protege.webprotege.event.EventTag;
55

6-
import java.util.List;
7-
8-
public record EventList <E extends Event> (EventTag startTag, List<E> events, EventTag endTag){
6+
/**
7+
* The window of events the client receives. The gateway only relays the events — it never acts on
8+
* their contents — so they are carried as raw JSON rather than typed objects: the producers (the
9+
* backend and the event-history service) and the browser client agree on the event payloads, and
10+
* forcing them through this service's own event model just makes delivery fail whenever the
11+
* gateway's model drifts from the producer's serialization.
12+
*/
13+
public record EventList(EventTag startTag, JsonNode events, EventTag endTag) {
914
}

src/main/java/edu/stanford/protege/webprotege/gateway/websocket/dto/SequencedPackagedProjectChangeEvent.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package edu.stanford.protege.webprotege.gateway.websocket.dto;
22

33
import com.fasterxml.jackson.annotation.JsonTypeName;
4+
import com.fasterxml.jackson.databind.JsonNode;
45
import edu.stanford.protege.webprotege.common.EventId;
56
import edu.stanford.protege.webprotege.common.ProjectEvent;
67
import edu.stanford.protege.webprotege.common.ProjectId;
78

89
import javax.annotation.Nonnull;
9-
import java.util.List;
1010

1111
/**
1212
* A {@link PackagedProjectChangeEvent} enriched with the per-project sequence ordinal assigned to the
@@ -16,10 +16,12 @@
1616
* <p>
1717
* This is a gateway-local mirror of the producer's event; the field names ({@code projectId},
1818
* {@code eventId}, {@code sequenceNumber}, {@code projectEvents}) are the wire contract and must
19-
* match the producer exactly.
19+
* match the producer exactly. The inner events stay raw JSON on purpose: the gateway only relays
20+
* them, and deserializing them into this service's own event model breaks delivery whenever that
21+
* model drifts from the producer's serialization.
2022
*/
2123
@JsonTypeName(SequencedPackagedProjectChangeEvent.CHANNEL)
22-
public record SequencedPackagedProjectChangeEvent(ProjectId projectId, EventId eventId, int sequenceNumber, List<ProjectEvent> projectEvents) implements ProjectEvent {
24+
public record SequencedPackagedProjectChangeEvent(ProjectId projectId, EventId eventId, int sequenceNumber, JsonNode projectEvents) implements ProjectEvent {
2325

2426
public final static String CHANNEL = "webprotege.events.projects.SequencedPackagedProjectChange";
2527

src/test/java/edu/stanford/protege/webprotege/gateway/sse/HistoryReplaySseCatchUpServiceTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package edu.stanford.protege.webprotege.gateway.sse;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
45
import com.google.common.util.concurrent.MoreExecutors;
56
import edu.stanford.protege.webprotege.common.ProjectId;
67
import edu.stanford.protege.webprotege.common.UserId;
@@ -122,13 +123,13 @@ void failedHistoryQueryKeepsStreamLiveInsteadOfKillingIt() throws IOException {
122123

123124
private static ProjectEventsQueryResponse liveResponse() {
124125
ProjectEventsQueryResponse response = new ProjectEventsQueryResponse();
125-
response.events = new EventList<>(EventTag.getFirst(), List.of(), EventTag.get(1));
126+
response.events = new EventList(EventTag.getFirst(), JsonNodeFactory.instance.arrayNode(), EventTag.get(1));
126127
return response;
127128
}
128129

129130
private static ProjectEventsQueryResponse historyResponse(int startTag, int endTag) {
130131
ProjectEventsQueryResponse response = new ProjectEventsQueryResponse();
131-
response.events = new EventList<>(EventTag.get(startTag), List.of(), EventTag.get(endTag));
132+
response.events = new EventList(EventTag.get(startTag), JsonNodeFactory.instance.arrayNode(), EventTag.get(endTag));
132133
return response;
133134
}
134135

src/test/java/edu/stanford/protege/webprotege/gateway/sse/SseStreamRegistryTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package edu.stanford.protege.webprotege.gateway.sse;
22

3+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
34
import com.fasterxml.jackson.databind.ObjectMapper;
45
import edu.stanford.protege.webprotege.common.ProjectId;
56
import edu.stanford.protege.webprotege.common.UserId;
@@ -176,7 +177,7 @@ void completeCatchUpWithoutReplayFlushesBufferedThenResumesLiveDelivery() throws
176177

177178
private static ProjectEventsQueryResponse response() {
178179
ProjectEventsQueryResponse response = new ProjectEventsQueryResponse();
179-
response.events = new EventList<>(EventTag.getFirst(), List.of(), EventTag.get(1));
180+
response.events = new EventList(EventTag.getFirst(), JsonNodeFactory.instance.arrayNode(), EventTag.get(1));
180181
return response;
181182
}
182183

src/test/java/edu/stanford/protege/webprotege/gateway/websocket/ProjectChangedEmitterHandlerTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ void setUp() {
6666
projectId,
6767
new OWLClassImpl(IRI.create("http://www.example.org/R9UuCy8Vzvft2f4fc67VwGs")),
6868
new ArrayList<>());
69-
sequencedEvent = new SequencedPackagedProjectChangeEvent(projectId, eventId, SEQUENCE_NUMBER, List.of(entityTagsChangedEvent));
69+
sequencedEvent = new SequencedPackagedProjectChangeEvent(projectId, eventId, SEQUENCE_NUMBER,
70+
objectMapper.valueToTree(List.of(entityTagsChangedEvent)));
7071
}
7172

7273
@Test
@@ -133,7 +134,7 @@ void GIVEN_sequencedEvent_WHEN_handleEvent_THEN_publishedToSseRegistryWithSequen
133134
ArgumentCaptor<ProjectEventsQueryResponse> sseCaptor = ArgumentCaptor.forClass(ProjectEventsQueryResponse.class);
134135
verify(sseStreamRegistry).publish(eq(projectId), eq((long) SEQUENCE_NUMBER), sseCaptor.capture());
135136

136-
EventList<?> events = sseCaptor.getValue().events;
137+
EventList events = sseCaptor.getValue().events;
137138
assertEquals(SEQUENCE_NUMBER - 1, events.startTag().getOrdinal());
138139
assertEquals(SEQUENCE_NUMBER, events.endTag().getOrdinal());
139140
assertEquals(sequencedEvent.projectEvents(), events.events());

0 commit comments

Comments
 (0)