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

openmrs / openmrs-core / 29049688645

09 Jul 2026 08:57PM UTC coverage: 63.783% (+0.006%) from 63.777%
29049688645

push

github

web-flow
TRUNK-6465: Prevent OrderServiceTest from deadlocking the connection pool (2.9.x) (#6286)

* TRUNK-6465: Prevent OrderServiceTest from deadlocking the connection pool

getNewOrderNumber_shouldAlwaysReturnUniqueOrderNumbersWhenCalledMultipleTimesWithoutSavingOrders
started 50 raw threads. Each call transiently holds two pooled
connections: one for the getNewOrderNumber transaction and one for the
REQUIRES_NEW transaction that increments the order number seed. With
hibernate.c3p0.max_size=50 and c3p0's default checkoutTimeout of 0
(wait forever), 50 concurrent outer transactions can drain the pool so
that every thread holds one connection while waiting eternally for a
second one nobody can release. The surefire fork then hangs silently
until the GitHub Actions 6 hour limit kills the job, which is what has
been wedging the Java 11 matrix leg (and historically Java 8, which was
skipped instead of fixed).

Run the 50 calls on a bounded pool of 20 threads so peak connection
demand stays below the pool cap, fail fast after 30 seconds instead of
hanging, and surface worker exceptions instead of swallowing them. This
removes the Java 8 skip since the deadlock is now structurally
impossible, and adds timeout-minutes to the build workflow so any
future wedge costs 40 minutes rather than 6 hours.


Claude-Session: https://claude.ai/code/session_01CQKxQruWri68zismbxhxW3

* TRUNK-6465: Harden the concurrency test failure path

Await executor termination after shutdownNow so straggler workers cannot
outlive the test method and muddy diagnosis of an original failure, turn
a bare TimeoutException on a wedge into an assertion failure that names
the connection pool deadlock and the ticket, and raise the workflow
timeout from 40 to 60 minutes so a degraded runner day (windows legs
normally reach 21 minutes) cannot false-positive the cap and fail-fast
cancel the sibling matrix legs.


Claude-Session: https://claude.ai/code/session_01CQKxQruWri68zismbxhxW3

* TRUNK-6465: P... (continued)

24078 of 37750 relevant lines covered (63.78%)

0.64 hits per line

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

75.44
/api/src/main/java/org/openmrs/Drug.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;
11

12
import java.util.Collection;
13
import java.util.LinkedHashSet;
14
import java.util.Locale;
15
import java.util.Set;
16

17
import org.apache.commons.lang3.StringUtils;
18
import org.codehaus.jackson.annotate.JsonIgnore;
19
import org.hibernate.envers.Audited;
20
import org.hibernate.search.mapper.pojo.automaticindexing.ReindexOnUpdate;
21
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.AssociationInverseSide;
22
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
23
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
24
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded;
25
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexingDependency;
26
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ObjectPath;
27
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyValue;
28
import org.openmrs.api.context.Context;
29

30
/**
31
 * Drug
32
 */
33
@Indexed
34
@Audited
35
public class Drug extends BaseChangeableOpenmrsMetadata {
36
        
37
        public static final long serialVersionUID = 285L;
38
        
39
        // Fields
40
        @DocumentId
41
        private Integer drugId;
42
        
43
        private Boolean combination = false;
1✔
44
        
45
        private Concept dosageForm;
46
        
47
        private Double maximumDailyDose;
48
        
49
        private Double minimumDailyDose;
50
        
51
        private String strength;
52
        
53
        private Concept doseLimitUnits;
54
        
55
        @IndexedEmbedded(includePaths = "conceptId", includeEmbeddedObjectId = true)
56
        @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW)
57
        private Concept concept;
58
        
59
        @IndexedEmbedded
60
        @AssociationInverseSide(inversePath = @ObjectPath({
61
                @PropertyValue(propertyName = "drug")
62
        }))
63
        private Set<DrugReferenceMap> drugReferenceMaps;
64
        
65
        private Collection<DrugIngredient> ingredients;
66
        
67
        // Constructors
68
        
69
        /** default constructor */
70
        public Drug() {
1✔
71
                ingredients = new LinkedHashSet<>();
1✔
72
        }
1✔
73
        
74
        /** constructor with id */
75
        public Drug(Integer drugId) {
×
76
                this.drugId = drugId;
×
77
        }
×
78
        
79
        // Property accessors
80
        
81
        /**
82
         * Gets the internal identification number for this drug
83
         *
84
         * @return Integer
85
         */
86
        public Integer getDrugId() {
87
                return this.drugId;
1✔
88
        }
89
        
90
        /**
91
         * Sets the internal identification number for this drug
92
         *
93
         * @param drugId
94
         */
95
        public void setDrugId(Integer drugId) {
96
                this.drugId = drugId;
1✔
97
        }
1✔
98
        
99
        /**
100
         * Gets the entries concept drug name in the form of CONCEPTNAME (Drug: DRUGNAME)
101
         * 
102
         * @param locale
103
         * @return full drug name (with concept name appended)
104
         */
105
        public String getFullName(Locale locale) {
106
                if (concept == null) {
×
107
                        return getName();
×
108
                } else {
109
                        return getName() + " (" + concept.getName(locale).getName() + ")";
×
110
                }
111
        }
112
        
113
        /**
114
         * Gets whether or not this is a combination drug
115
         *
116
         * @return Boolean
117
         * 
118
         * @deprecated as of 2.0, use {@link #getCombination()}
119
         */
120
        @Deprecated
121
        @JsonIgnore
122
        public Boolean isCombination() {
123
                return getCombination();
×
124
        }
125
        
126
        public Boolean getCombination() {
127
                return combination;
1✔
128
        }
129
        
130
        /**
131
         * Sets whether or not this is a combination drug
132
         *
133
         * @param combination
134
         */
135
        public void setCombination(Boolean combination) {
136
                this.combination = combination;
1✔
137
        }
1✔
138
        
139
        /**
140
         * Gets the strength
141
         *
142
         * @return String
143
         * @since 1.10
144
         */
145
        public String getStrength() {
146
                return strength;
1✔
147
        }
148
        
149
        /**
150
         * Sets the strength
151
         *
152
         * @param strength
153
         * @since 1.10
154
         */
155
        public void setStrength(String strength) {
156
                this.strength = strength;
1✔
157
        }
1✔
158
        
159
        /**
160
         * Gets the concept this drug is tied to
161
         *
162
         * @return Concept
163
         */
164
        public Concept getConcept() {
165
                return this.concept;
1✔
166
        }
167
        
168
        /**
169
         * Sets the concept this drug is tied to
170
         *
171
         * @param concept
172
         */
173
        public void setConcept(Concept concept) {
174
                this.concept = concept;
1✔
175
        }
1✔
176
        
177
        public Concept getDosageForm() {
178
                return dosageForm;
1✔
179
        }
180
        
181
        public void setDosageForm(Concept dosageForm) {
182
                this.dosageForm = dosageForm;
1✔
183
        }
1✔
184
        
185
        public Double getMaximumDailyDose() {
186
                return maximumDailyDose;
1✔
187
        }
188
        
189
        public void setMaximumDailyDose(Double maximumDailyDose) {
190
                this.maximumDailyDose = maximumDailyDose;
1✔
191
        }
1✔
192
        
193
        public Double getMinimumDailyDose() {
194
                return minimumDailyDose;
1✔
195
        }
196
        
197
        public void setMinimumDailyDose(Double minimumDailyDose) {
198
                this.minimumDailyDose = minimumDailyDose;
1✔
199
        }
1✔
200
        
201
        /**
202
         * @return Returns the ingredients
203
         * @since 1.10
204
         */
205
        public Collection<DrugIngredient> getIngredients() {
206
                return ingredients;
1✔
207
        }
208
        
209
        /**
210
         * @param ingredients
211
         *            The ingredients to set
212
         * @since 1.10
213
         */
214
        public void setIngredients(Collection<DrugIngredient> ingredients) {
215
                this.ingredients = ingredients;
1✔
216
        }
1✔
217
        
218
        /**
219
         * @since 1.5
220
         * @see org.openmrs.OpenmrsObject#getId()
221
         */
222
        @Override
223
        public Integer getId() {
224
                
225
                return getDrugId();
1✔
226
        }
227
        
228
        /**
229
         * @since 1.5
230
         * @see org.openmrs.OpenmrsObject#setId(java.lang.Integer)
231
         */
232
        @Override
233
        public void setId(Integer id) {
234
                setDrugId(id);
×
235
        }
×
236
        
237
        /**
238
         * Convenience method that returns a display name for the drug, defaults to drug.name
239
         *
240
         * @return the display name
241
         * @since 1.8.5, 1.9.4, 1.10
242
         */
243
        public String getDisplayName() {
244
                if (StringUtils.isNotBlank(getName())) {
×
245
                        return getName();
×
246
                }
247
                if (getConcept() != null) {
×
248
                        return getConcept().getName().getName();
×
249
                }
250
                return "";
×
251
        }
252
        
253
        /**
254
         * @return Returns the drugReferenceMaps.
255
         * @since 1.10
256
         */
257
        public Set<DrugReferenceMap> getDrugReferenceMaps() {
258
                if (drugReferenceMaps == null) {
1✔
259
                        drugReferenceMaps = new LinkedHashSet<>();
1✔
260
                }
261
                return drugReferenceMaps;
1✔
262
        }
263
        
264
        /**
265
         * @param drugReferenceMaps The drugReferenceMaps to set.
266
         * @since 1.10
267
         */
268
        public void setDrugReferenceMaps(Set<DrugReferenceMap> drugReferenceMaps) {
269
                this.drugReferenceMaps = drugReferenceMaps;
1✔
270
        }
1✔
271
        
272
        /**
273
         * Add the given DrugReferenceMap object to this drug's list of drug reference mappings. If there is
274
         * already a corresponding DrugReferenceMap object for this concept, this one will not be added.
275
         *
276
         * @param drugReferenceMap
277
         * @since 1.10
278
         *
279
         * <strong>Should</strong> set drug as the drug to which a mapping is being added
280
         *
281
         * <strong>Should</strong> should not add duplicate drug reference maps
282
         */
283
        public void addDrugReferenceMap(DrugReferenceMap drugReferenceMap) {
284
                if (drugReferenceMap != null && !getDrugReferenceMaps().contains(drugReferenceMap)) {
1✔
285
                        drugReferenceMap.setDrug(this);
1✔
286
                        if (drugReferenceMap.getConceptMapType() == null) {
1✔
287
                                drugReferenceMap.setConceptMapType(Context.getConceptService().getDefaultConceptMapType());
1✔
288
                        }
289
                        getDrugReferenceMaps().add(drugReferenceMap);
1✔
290
                }
291
        }
1✔
292
        
293
        /**
294
         * Gets the doseLimitUnits which represents the units of the existing maximumDailyDose and
295
         * minimumDailyDose
296
         * 
297
         * @return the doseLimitUnits.
298
         * @since 2.3.0
299
         */
300
        public Concept getDoseLimitUnits() {
301
                return doseLimitUnits;
1✔
302
        }
303
        
304
        /**
305
         * Sets the doseLimitUnits which represents the units of the existing maximumDailyDose and
306
         * minimumDailyDose
307
         * 
308
         * @param doseLimitUnits The doseLimitUnits to set.
309
         * @since 2.3.0
310
         */
311
        public void setDoseLimitUnits(Concept doseLimitUnits) {
312
                this.doseLimitUnits = doseLimitUnits;
1✔
313
        }
1✔
314
}
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