• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 27147200794

08 Jun 2026 03:09PM UTC coverage: 63.494% (-10.1%) from 73.596%
27147200794

push

github

web-flow
TRUNK-6516: Provide CDC mechanism with Debezium (#6151)

16 of 78 new or added lines in 8 files covered. (20.51%)

39 existing lines in 3 files now uncovered.

23804 of 37490 relevant lines covered (63.49%)

0.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/api/src/main/java/org/openmrs/event/CDCEvent.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.event;
11

12
import org.codehaus.jackson.annotate.JsonIgnore;
13
import org.jspecify.annotations.Nullable;
14
import org.springframework.core.ResolvableType;
15
import org.springframework.core.ResolvableTypeProvider;
16

17
import java.util.Map;
18
import java.util.Set;
19

20
/**
21
 * Issued by the CDC engine (if available, see e.g. https://github.com/openmrs/openmrs-module-debezium)
22
 * <p>
23
 * Please note that CDC events are usually not happening inside an application transaction rather
24
 * they are fetched from DB by a separate process e.g. Debezium. For this reason it does not make sense
25
 * to use {@link org.springframework.transaction.event.TransactionalEventListener} to process them.
26
 * <p>
27
 * If you want to guarantee delivery, please use {@link org.springframework.context.event.EventListener} 
28
 * so that an event is processed in the same thread as the publisher and if the processing fails, it will be
29
 * re-tried by the publisher. It is recommended only for fast enough listeners so that publisher is not
30
 * blocked for too long, and it keeps up with the events queue.
31
 * <p>
32
 * If you combine {@link org.springframework.context.event.EventListener} with {@link org.springframework.scheduling.annotation.Async}
33
 * you may run longer processing, but you lose the ability to re-try if anything fails, and in worst case you may lose the event.
34
 * <p>
35
 * For longer running processing with guaranteed delivery you may use {@link org.openmrs.event.outbox.OutboxEventListener}
36
 * to persist events to the database and have them processed asynchronously or publish events with 
37
 * {@link org.springframework.context.event.EventListener} to a message broker for asynchronous processing.
38
 * The latter is more performant, and it can also be used to exchange messages with external systems.
39
 * 
40
 * @param <T> entityType associated with this event
41
 * 
42
 * @since 2.9.0
43
 */
44
public class CDCEvent<T> extends BaseEvent implements ResolvableTypeProvider {
45
        private static final long serialVersionUID = 1L;
46
        
47
        private String snapshot;
48
        
49
        private Class<T> entityType;
50
        
51
        private String transactionId;
52

53
        private Operation operation;
54
        
55
        private String tableName;
56
        
57
        private Map<String, Object> primaryKey;
58

59
        private Map<String, Object> newState;
60
        
61
        private Map<String, Object> previousState;
62
        
63
        /**
64
         * Default constructor for deserialization.
65
         */
NEW
66
        public CDCEvent() {
×
NEW
67
        }
×
68
        
NEW
69
        public CDCEvent(Class<T> entityType) {
×
NEW
70
                this.entityType = entityType;
×
NEW
71
        }
×
72

73
        public CDCEvent(Class<T> entityType, Set<String> tags) {
NEW
74
                super(tags);
×
NEW
75
                this.entityType = entityType;
×
NEW
76
        }
×
77

78
        public void setPrimaryKey(Map<String, Object> primaryKey) {
NEW
79
                this.primaryKey = primaryKey;
×
NEW
80
        }
×
81

82
        public Map<String, Object> getPrimaryKey() {
NEW
83
                return primaryKey;
×
84
        }
85
        
86
        public boolean isCompositePrimaryKey() {
NEW
87
                return primaryKey != null && primaryKey.size() > 1;
×
88
        }
89

90
        public void setEntityType(Class<T> entityType) {
NEW
91
                this.entityType = entityType;
×
NEW
92
        }
×
93

94
        public Class<T> getEntityType() {
NEW
95
                return entityType;
×
96
        }
97

98
        public void setTransactionId(String transactionId) {
NEW
99
                this.transactionId = transactionId;
×
NEW
100
        }
×
101

102
        public String getTransactionId() {
NEW
103
                return transactionId;
×
104
        }
105

106
        public void setTableName(String tableName) {
NEW
107
                this.tableName = tableName;
×
NEW
108
        }
×
109

110
        public String getTableName() {
NEW
111
                return tableName;
×
112
        }
113

114
        public void setOperation(Operation operation) {
NEW
115
                this.operation = operation;
×
NEW
116
        }
×
117

118
        public Operation getOperation() {
NEW
119
                return operation;
×
120
        }
121

122
        /**
123
         * Indicates that the event is an initial data synchronization.
124
         * 
125
         * @return true, first or last for snapshot event, false or null otherwise
126
         */
127
        public String getSnapshot() {
NEW
128
                return snapshot;
×
129
        }
130

131
        public void setSnapshot(String snapshot) {
NEW
132
                this.snapshot = snapshot;
×
NEW
133
        }
×
134

135
        public Map<String, Object> getNewState() {
NEW
136
                return newState;
×
137
        }
138

139
        public void setNewState(Map<String, Object> newState) {
NEW
140
                this.newState = newState;
×
NEW
141
        }
×
142

143
        public Map<String, Object> getPreviousState() {
NEW
144
                return previousState;
×
145
        }
146

147
        public void setPreviousState(Map<String, Object> previousState) {
NEW
148
                this.previousState = previousState;
×
NEW
149
        }
×
150
        
NEW
151
        public enum Operation {
×
NEW
152
                READ, CREATE, UPDATE, DELETE, TRUNCATE 
×
153
        }
154

155
        @JsonIgnore
156
        @Override
157
        public @Nullable ResolvableType getResolvableType() {
NEW
158
                if (entityType != null && getClass().getTypeParameters().length == 1) {
×
NEW
159
                        return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forClass(entityType));
×
160
                } else {
NEW
161
                        return ResolvableType.forClass(getClass());
×
162
                }
163
        }
164
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc