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

openmrs / openmrs-core / 36872660172

01 Oct 2026 01:58PM UTC coverage: 66.293% (+0.2%) from 66.046%
36872660172

push

github

ibacher
TRUNK-6707: Implement full caching for Global Properties (#6598)

219 of 229 new or added lines in 7 files covered. (95.63%)

1 existing line in 1 file now uncovered.

25757 of 38853 relevant lines covered (66.29%)

0.66 hits per line

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

66.19
/api/src/main/java/org/openmrs/api/impl/AdministrationServiceImpl.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.api.impl;
11

12
import java.io.UnsupportedEncodingException;
13
import java.net.InetAddress;
14
import java.net.UnknownHostException;
15
import java.text.SimpleDateFormat;
16
import java.util.ArrayList;
17
import java.util.Arrays;
18
import java.util.Calendar;
19
import java.util.Collection;
20
import java.util.HashMap;
21
import java.util.HashSet;
22
import java.util.Iterator;
23
import java.util.LinkedHashMap;
24
import java.util.LinkedHashSet;
25
import java.util.List;
26
import java.util.Locale;
27
import java.util.Map;
28
import java.util.Objects;
29
import java.util.Properties;
30
import java.util.Set;
31
import java.util.SortedMap;
32
import java.util.TreeMap;
33

34
import org.apache.commons.lang3.StringUtils;
35
import org.openmrs.ConceptSource;
36
import org.openmrs.GlobalProperty;
37
import org.openmrs.ImplementationId;
38
import org.openmrs.OpenmrsData;
39
import org.openmrs.OpenmrsMetadata;
40
import org.openmrs.OpenmrsObject;
41
import org.openmrs.Privilege;
42
import org.openmrs.User;
43
import org.openmrs.api.APIException;
44
import org.openmrs.api.AdministrationService;
45
import org.openmrs.api.EventListeners;
46
import org.openmrs.api.GlobalPropertyListener;
47
import org.openmrs.api.cache.GlobalPropertyCache;
48
import org.openmrs.api.context.Context;
49
import org.openmrs.api.db.AdministrationDAO;
50
import org.openmrs.customdatatype.CustomDatatype;
51
import org.openmrs.customdatatype.CustomDatatypeUtil;
52
import org.openmrs.customdatatype.CustomValueDescriptor;
53
import org.openmrs.customdatatype.Customizable;
54
import org.openmrs.customdatatype.SingleCustomValue;
55
import org.openmrs.layout.LayoutSupport;
56
import org.openmrs.layout.LayoutTemplate;
57
import org.openmrs.messagesource.PresentationMessage;
58
import org.openmrs.module.Module;
59
import org.openmrs.module.ModuleFactory;
60
import org.openmrs.module.ModuleUtil;
61
import org.openmrs.obs.ComplexData;
62
import org.openmrs.person.PersonMergeLogData;
63
import org.openmrs.util.DatabaseUpdateException;
64
import org.openmrs.util.DatabaseUpdater;
65
import org.openmrs.util.HttpClient;
66
import org.openmrs.util.LocaleUtility;
67
import org.openmrs.util.OpenmrsConstants;
68
import org.slf4j.Logger;
69
import org.slf4j.LoggerFactory;
70
import org.springframework.cache.annotation.CacheEvict;
71
import org.springframework.cache.annotation.Cacheable;
72
import org.springframework.transaction.annotation.Propagation;
73
import org.springframework.transaction.annotation.Transactional;
74
import org.springframework.validation.Errors;
75

76
/**
77
 * Default implementation of the administration services. This class should not be used on its own.
78
 * The current OpenMRS implementation should be fetched from the Context
79
 * 
80
 * @see org.openmrs.api.AdministrationService
81
 * @see org.openmrs.api.context.Context
82
 */
83
@Transactional
84
public class AdministrationServiceImpl extends BaseOpenmrsService implements AdministrationService, GlobalPropertyListener {
85
        
86
        private static final Logger log = LoggerFactory.getLogger(AdministrationServiceImpl.class);
1 ✔
87

88
        protected AdministrationDAO dao;
89
        
90
        private EventListeners eventListeners;
91
        
92
        /**
93
         * An always up-to-date collection of the allowed locales.
94
         */
95
        private GlobalLocaleList globalLocaleList;
96
        
97
        private HttpClient implementationIdHttpClient;
98
        
99
        private GlobalPropertyCache globalPropertyCache;
100
        
101
        /**
102
         * Default empty constructor
103
         */
104
        public AdministrationServiceImpl() {
1 ✔
105
        }
1 ✔
106
        
107
        /**
108
         * @see org.openmrs.api.AdministrationService#setAdministrationDAO(org.openmrs.api.db.AdministrationDAO)
109
         */
110
        @Override
111
        public void setAdministrationDAO(AdministrationDAO dao) {
112
                this.dao = dao;
1 ✔
113
        }
1 ✔
114
        
115
        public void setGlobalPropertyCache(GlobalPropertyCache globalPropertyCache) {
116
                this.globalPropertyCache = globalPropertyCache;
1 ✔
117
        }
1 ✔
118
        
119
        public void setEventListeners(EventListeners eventListeners) {
120
                this.eventListeners = eventListeners;
1 ✔
121
        }
1 ✔
122
                
123
        /**
124
         * Static-ish variable used to cache the system variables. This is not static so that every time
125
         * a module is loaded or removed the variable is destroyed (along with the administration
126
         * service) and recreated the next time it is called
127
         */
128
        protected SortedMap<String, String> systemVariables = null;
1 ✔
129
        
130
        /**
131
         * Set of locales which can be used to present messages in the user interface. Created lazily as
132
         * needed by {@link #getAllowedLocales()}.
133
         */
134
        private Set<Locale> presentationLocales;
135
        
136
        /**
137
         * @see org.openmrs.api.AdministrationService#getSystemVariables()
138
         */
139
        @Override
140
        @Transactional(readOnly = true)
141
        public SortedMap<String, String> getSystemVariables() throws APIException {
142
                if (systemVariables == null) {
1 ✔
143
                        systemVariables = new TreeMap<>();
1 ✔
144
                        
145
                        // Added the server's fully qualified domain name
146
                        try {
147
                                systemVariables.put("OPENMRS_HOSTNAME", InetAddress.getLocalHost().getCanonicalHostName());
1 ✔
148
                        }
149
                        catch (UnknownHostException e) {
×
150
                                systemVariables.put("OPENMRS_HOSTNAME", "Unknown host: " + e.getMessage());
×
151
                        }
1 ✔
152
                        systemVariables.put("OPENMRS_VERSION", String.valueOf(OpenmrsConstants.OPENMRS_VERSION));
1 ✔
153
                        systemVariables.put("DATABASE_NAME", OpenmrsConstants.DATABASE_NAME);
1 ✔
154
                        systemVariables.put("DATABASE_BUSINESS_NAME", OpenmrsConstants.DATABASE_BUSINESS_NAME);
1 ✔
155
                        systemVariables.put("OBSCURE_PATIENTS", String.valueOf(OpenmrsConstants.OBSCURE_PATIENTS));
1 ✔
156
                        systemVariables.put("OBSCURE_PATIENTS_FAMILY_NAME", OpenmrsConstants.OBSCURE_PATIENTS_FAMILY_NAME);
1 ✔
157
                        systemVariables.put("OBSCURE_PATIENTS_GIVEN_NAME", OpenmrsConstants.OBSCURE_PATIENTS_GIVEN_NAME);
1 ✔
158
                        systemVariables.put("OBSCURE_PATIENTS_MIDDLE_NAME", OpenmrsConstants.OBSCURE_PATIENTS_MIDDLE_NAME);
1 ✔
159
                        systemVariables.put("MODULE_REPOSITORY_PATH", ModuleUtil.getModuleRepository().getAbsolutePath());
1 ✔
160
                        systemVariables.put("OPERATING_SYSTEM_KEY", String.valueOf(OpenmrsConstants.OPERATING_SYSTEM_KEY));
1 ✔
161
                        systemVariables.put("OPERATING_SYSTEM", String.valueOf(OpenmrsConstants.OPERATING_SYSTEM));
1 ✔
162
                }
163
                
164
                return systemVariables;
1 ✔
165
        }
166
        
167
        /**
168
         * @see org.openmrs.api.AdministrationService#getGlobalProperty(java.lang.String)
169
         */
170
        @Override
171
        // a cache hit needs no transaction, and a miss can read through the caller's session or one opened
172
        // for the call
173
        @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
174
        public String getGlobalProperty(String propertyName) throws APIException {
175
                // This method should not have any authorization check
176
                if (propertyName == null) {
1 ✔
177
                        return null;
1 ✔
178
                }
179
                
180
                GlobalPropertyCache.Entry gp = globalPropertyCache.get(propertyName);
1 ✔
181
                checkCanView(gp, propertyName);
1 ✔
182
                return gp.getValue();
1 ✔
183
        }
184
        
185
        private void checkCanView(GlobalPropertyCache.Entry gp, String propertyName) {
186
                if (gp.getViewPrivilege() != null && !Context.hasPrivilege(gp.getViewPrivilege(), false)) {
1 ✔
187
                        throw new APIException("GlobalProperty.error.privilege.required.view",
1 ✔
188
                                new Object[] { gp.getViewPrivilege(), propertyName });
1 ✔
189
                }
190
        }
1 ✔
191
        
192
        private boolean canViewGlobalProperty(GlobalProperty property) {
193
                if (property.getViewPrivilege() == null) {
1 ✔
194
                        return true;
1 ✔
195
                }
196

197
                return Context.hasPrivilege(property.getViewPrivilege().getPrivilege(), false);
1 ✔
198
        }
199
        
200
        private boolean canDeleteGlobalProperty(GlobalProperty property) {
201
                if (property.getDeletePrivilege() == null) {
1 ✔
202
                        return true;
1 ✔
203
                }
204

205
                return Context.hasPrivilege(property.getDeletePrivilege().getPrivilege(), false);
1 ✔
206
        }
207
        
208
        private boolean canEditGlobalProperty(GlobalProperty property) {
209
                if (property.getEditPrivilege() == null) {
1 ✔
210
                        return true;
1 ✔
211
                }
212

213
                return Context.hasPrivilege(property.getEditPrivilege().getPrivilege(), false);
1 ✔
214
        }
215
        
216
        private List<GlobalProperty> filterGlobalPropertiesByViewPrivilege(List<GlobalProperty> properties) {
217
                if (properties != null) {
1 ✔
218
                        for (Iterator<GlobalProperty> iterator = properties.iterator(); iterator.hasNext();) {
1 ✔
219
                                GlobalProperty property = iterator.next();
1 ✔
220
                                Privilege vp = property.getViewPrivilege();
1 ✔
221
                                if (vp != null && !Context.hasPrivilege(vp.getPrivilege(), false)) {
1 ✔
222
                                        iterator.remove();
1 ✔
223
                                }
224
                        }
1 ✔
225
                }
226
                return properties;
1 ✔
227
        }
228
        
229
        /**
230
         * @see org.openmrs.api.AdministrationService#getGlobalProperty(java.lang.String,
231
         *      java.lang.String)
232
         */
233
        @Override
234
        @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
235
        public String getGlobalProperty(String propertyName, String defaultValue) throws APIException {
236
                String s = Context.getAdministrationService().getGlobalProperty(propertyName);
1 ✔
237
                if (s == null) {
1 ✔
238
                        return defaultValue;
1 ✔
239
                }
240
                return s;
1 ✔
241
        }
242
        
243
        /**
244
         * @see org.openmrs.api.AdministrationService#getGlobalPropertyObject(java.lang.String)
245
         */
246
        @Override
247
        @Transactional(readOnly = true)
248
        public GlobalProperty getGlobalPropertyObject(String propertyName) {
249
                GlobalProperty gp = dao.getGlobalPropertyObject(propertyName);
1 ✔
250
                if (gp != null) {
1 ✔
251
                        if (canViewGlobalProperty(gp)) {
1 ✔
252
                                return gp;
1 ✔
253
                        } else {
254
                                throw new APIException("GlobalProperty.error.privilege.required.view", new Object[] {
×
255
                                        gp.getViewPrivilege().getPrivilege(), propertyName });
×
256
                        }
257
                } else {
258
                        return null;
1 ✔
259
                }
260
        }
261
        
262
        /**
263
         * @see org.openmrs.api.AdministrationService#setGlobalProperty(java.lang.String,
264
         *      java.lang.String)
265
         */
266
        @Override
267
        public void setGlobalProperty(String propertyName, String propertyValue) throws APIException {
268
                GlobalProperty gp = Context.getAdministrationService().getGlobalPropertyObject(propertyName);
1 ✔
269
                if (gp == null) {
1 ✔
270
                        gp = new GlobalProperty();
1 ✔
271
                        gp.setProperty(propertyName);
1 ✔
272
                }
273
                gp.setPropertyValue(propertyValue);
1 ✔
274
                Context.getAdministrationService().saveGlobalProperty(gp);
1 ✔
275
        }
1 ✔
276
        
277
        /**
278
         * @see org.openmrs.api.AdministrationService#updateGlobalProperty(java.lang.String,
279
         *      java.lang.String)
280
         */
281
        @Override
282
        public void updateGlobalProperty(String propertyName, String propertyValue) throws IllegalStateException {
283
                GlobalProperty gp = Context.getAdministrationService().getGlobalPropertyObject(propertyName);
1 ✔
284
                if (gp == null) {
1 ✔
285
                        throw new IllegalStateException("Global property with the given propertyName does not exist" + propertyName);
1 ✔
286
                }
287
                
288
                if (!canEditGlobalProperty(gp)) {
1 ✔
289
                        throw new APIException("GlobalProperty.error.privilege.required.edit", new Object[] {
1 ✔
290
                                gp.getEditPrivilege().getPrivilege(), propertyName });
1 ✔
291
                }
292
                
293
                gp.setPropertyValue(propertyValue);
1 ✔
294
                dao.saveGlobalProperty(gp);
1 ✔
295
                globalPropertyCache.evict(propertyName);
1 ✔
296
        }
1 ✔
297
        
298
        /**
299
         * @see org.openmrs.api.AdministrationService#getAllGlobalProperties()
300
         */
301
        @Override
302
        @Transactional(readOnly = true)
303
        public List<GlobalProperty> getAllGlobalProperties() throws APIException {
304
                return filterGlobalPropertiesByViewPrivilege(dao.getAllGlobalProperties());
1 ✔
305
        }
306
        
307
        /**
308
         * @see org.openmrs.api.AdministrationService#getGlobalPropertiesByPrefix(java.lang.String)
309
         */
310
        @Override
311
        @Transactional(readOnly = true)
312
        public List<GlobalProperty> getGlobalPropertiesByPrefix(String prefix) {
313
                return filterGlobalPropertiesByViewPrivilege(dao.getGlobalPropertiesByPrefix(prefix));
1 ✔
314
        }
315
        
316
        /**
317
         * @see org.openmrs.api.AdministrationService#getGlobalPropertiesBySuffix(java.lang.String)
318
         */
319
        @Override
320
        @Transactional(readOnly = true)
321
        public List<GlobalProperty> getGlobalPropertiesBySuffix(String suffix) {
322
                return filterGlobalPropertiesByViewPrivilege(dao.getGlobalPropertiesBySuffix(suffix));
1 ✔
323
        }
324
        
325
        /**
326
         * @see org.openmrs.api.AdministrationService#purgeGlobalProperty(org.openmrs.GlobalProperty)
327
         */
328
        @Override
329
        public void purgeGlobalProperty(GlobalProperty globalProperty) throws APIException {
330
                if (!canDeleteGlobalProperty(globalProperty)) {
1 ✔
331
                        throw new APIException("GlobalProperty.error.privilege.required.purge", new Object[] {
1 ✔
332
                                globalProperty.getDeletePrivilege().getPrivilege(), globalProperty.getProperty() });
1 ✔
333
                }
334
                
335
                notifyGlobalPropertyDelete(globalProperty.getProperty());
1 ✔
336
                dao.deleteGlobalProperty(globalProperty);
1 ✔
337
                globalPropertyCache.evict(globalProperty.getProperty());
1 ✔
338
        }
1 ✔
339
        
340
        /**
341
         * @see org.openmrs.api.AdministrationService#saveGlobalProperties(java.util.List)
342
         */
343
        @Override
344
        @CacheEvict(value = "userSearchLocales", allEntries = true)
345
        public List<GlobalProperty> saveGlobalProperties(List<GlobalProperty> props) throws APIException {
346
                log.debug("saving a list of global properties");
1 ✔
347
                
348
                // add all of the new properties
349
                for (GlobalProperty prop : props) {
1 ✔
350
                        if (prop.getProperty() != null && prop.getProperty().length() > 0) {
1 ✔
351
                                Context.getAdministrationService().saveGlobalProperty(prop);
1 ✔
352
                        }
353
                }
1 ✔
354
                
355
                return props;
1 ✔
356
        }
357
        
358
        /**
359
         * @see org.openmrs.api.AdministrationService#saveGlobalProperty(org.openmrs.GlobalProperty)
360
         */
361
        @Override
362
        @CacheEvict(value = "userSearchLocales", allEntries = true)
363
        public GlobalProperty saveGlobalProperty(GlobalProperty gp) throws APIException {
364

365
                if (!canEditGlobalProperty(gp)) {
1 ✔
366
                        throw new APIException("GlobalProperty.error.privilege.required.edit", new Object[] {
1 ✔
367
                                gp.getEditPrivilege().getPrivilege(), gp.getProperty() });
1 ✔
368
                }
369
                
370
                // only try to save it if the global property has a key
371
                if (gp.getProperty() != null && gp.getProperty().length() > 0) {
1 ✔
372
                        if (gp.getProperty().equals(OpenmrsConstants.GLOBAL_PROPERTY_LOCALE_ALLOWED_LIST)) {
1 ✔
373
                                if (gp.getPropertyValue() != null) {
1 ✔
374
                                        List<Locale> localeList = new ArrayList<>();
1 ✔
375
                                        
376
                                        for (String localeString : gp.getPropertyValue().split(",")) {
1 ✔
377
                                                localeList.add(LocaleUtility.fromSpecification(localeString.trim()));
1 ✔
378
                                        }
379
                                        if (!localeList.contains(LocaleUtility.getDefaultLocale())) {
1 ✔
380
                                                gp.setPropertyValue(StringUtils.join(getAllowedLocales(), ", "));
1 ✔
381
                                                throw new APIException(Context.getMessageSourceService().getMessage(
1 ✔
382
                                                    "general.locale.localeListNotIncludingDefaultLocale",
383
                                                    new Object[] { LocaleUtility.getDefaultLocale() }, null));
1 ✔
384
                                        }
385
                                }
1 ✔
386
                        } else if (gp.getProperty().equals(OpenmrsConstants.GLOBAL_PROPERTY_DEFAULT_LOCALE)
1 ✔
387
                                                && gp.getPropertyValue() != null) {
1 ✔
388
                        
389
                                        List<Locale> localeList = getAllowedLocales();
1 ✔
390
                                        
391
                                        if (!localeList.contains(LocaleUtility.fromSpecification(gp.getPropertyValue().trim()))) {
1 ✔
392
                                                String value = gp.getPropertyValue();
1 ✔
393
                                                gp.setPropertyValue(LocaleUtility.getDefaultLocale().toString());
1 ✔
394
                                                throw new APIException((Context.getMessageSourceService().getMessage(
1 ✔
395
                                                    "general.locale.defaultNotInAllowedLocalesList", new Object[] { value }, null)));
396
                                        }
397
                        }
398
                        
399
                        CustomDatatypeUtil.saveIfDirty(gp);
1 ✔
400
                        dao.saveGlobalProperty(gp);
1 ✔
401
                        globalPropertyCache.evict(gp.getProperty());
1 ✔
402
                        notifyGlobalPropertyChange(gp);
1 ✔
403
                        return gp;
1 ✔
404
                }
405
                
406
                return gp;
×
407
        }
408
        
409
        /**
410
         * @see org.openmrs.api.AdministrationService#executeSQL(java.lang.String, boolean)
411
         */
412
        @Override
413
        public List<List<Object>> executeSQL(String sql, boolean selectOnly) throws APIException {
414
                if (sql == null || "".equals(sql.trim())) {
1 ✔
415
                        return null;
1 ✔
416
                }
417
                
418
                return dao.executeSQL(sql, selectOnly);
1 ✔
419
        }
420
        
421
        /**
422
         * @see org.openmrs.api.AdministrationService#addGlobalPropertyListener(GlobalPropertyListener)
423
         */
424
        @Override
425
        public void addGlobalPropertyListener(GlobalPropertyListener listener) {
426
                eventListeners.getGlobalPropertyListeners().add(listener);
1 ✔
427
        }
1 ✔
428
        
429
        /**
430
         * @see org.openmrs.api.AdministrationService#removeGlobalPropertyListener(GlobalPropertyListener)
431
         */
432
        @Override
433
        public void removeGlobalPropertyListener(GlobalPropertyListener listener) {
434
                eventListeners.getGlobalPropertyListeners().remove(listener);
1 ✔
435
        }
1 ✔
436
        
437
        /**
438
         * Calls global property listeners registered for this create/change
439
         * 
440
         * @param gp
441
         */
442
        private void notifyGlobalPropertyChange(GlobalProperty gp) {
443
                for (GlobalPropertyListener listener : eventListeners.getGlobalPropertyListeners()) {
1 ✔
444
                        if (listener.supportsPropertyName(gp.getProperty())) {
1 ✔
445
                                listener.globalPropertyChanged(gp);
1 ✔
446
                        }
447
                }
1 ✔
448
        }
1 ✔
449
        
450
        /**
451
         * Calls global property listeners registered for this delete
452
         * 
453
         * @param propertyName
454
         */
455
        private void notifyGlobalPropertyDelete(String propertyName) {
456
                for (GlobalPropertyListener listener : eventListeners.getGlobalPropertyListeners()) {
1 ✔
457
                        if (listener.supportsPropertyName(propertyName)) {
1 ✔
458
                                listener.globalPropertyDeleted(propertyName);
1 ✔
459
                        }
460
                }
1 ✔
461
        }
1 ✔
462
        
463
        /**
464
         * @see org.openmrs.api.AdministrationService#getImplementationId()
465
         */
466
        @Override
467
        @Transactional(readOnly = true)
468
        public ImplementationId getImplementationId() throws APIException {
469
                String property = Context.getAdministrationService().getGlobalProperty(
1 ✔
470
                    OpenmrsConstants.GLOBAL_PROPERTY_IMPLEMENTATION_ID);
471
                
472
                // fail early if no gp has been defined yet
473
                if (property == null) {
1 ✔
474
                        return null;
1 ✔
475
                }
476
                
477
                try {
478
                        return Context.getSerializationService().getDefaultSerializer().deserialize(property, ImplementationId.class);
×
479
                }
480
                catch (Exception e) {
×
481
                        log.debug("Error while getting implementation id", e);
×
482
                }
483
                
484
                return null;
×
485
        }
486
        
487
        /**
488
         * @see org.openmrs.api.AdministrationService#setImplementationId(org.openmrs.ImplementationId)
489
         */
490
        @Override
491
        public void setImplementationId(ImplementationId implementationId) throws APIException {
492
                
493
                if (implementationId == null) {
1 ✔
494
                        return;
1 ✔
495
                }
496
                
497
                // check the validity of this implementation id with the server
498
                String description = implementationId.getDescription();
1 ✔
499
                try {
500
                        // check that source id is valid
501
                        description = checkImplementationIdValidity(implementationId.getImplementationId(), description,
1 ✔
502
                            implementationId.getPassphrase());
1 ✔
503
                        
504
                        // save the server's description back to this concept source object
505
                        implementationId.setDescription(description);
×
506
                        
507
                        boolean foundMatchingSource = false;
×
508
                        // loop over the concept sources to make sure one exists for this hl7Code/implementationId
509
                        List<ConceptSource> sources = Context.getConceptService().getAllConceptSources(false);
×
510
                        if (sources != null) {
×
511
                                for (ConceptSource source : sources) {
×
512
                                        if (implementationId.getImplementationId().equals(source.getHl7Code())) {
×
513
                                                foundMatchingSource = true;
×
514
                                        }
515
                                }
×
516
                        }
517
                        
518
                        // if no ConceptSource currently exists with this implementationId, save this implId
519
                        // as a new ConceptSource
520
                        if (!foundMatchingSource) {
×
521
                                ConceptSource newConceptSource = new ConceptSource();
×
522
                                newConceptSource.setName(implementationId.getName());
×
523
                                newConceptSource.setDescription(implementationId.getDescription());
×
524
                                newConceptSource.setHl7Code(implementationId.getImplementationId());
×
525
                                if (Context.getAuthenticatedUser() == null) {
×
526
                                        // (hackish)
527
                                        newConceptSource.setCreator(new User(1)); // fake the user because no one is logged in
×
528
                                }
529
                                Context.getConceptService().saveConceptSource(newConceptSource);
×
530
                        }
531
                        
532
                        // serialize and save the ImplementationId to the global properties table
533
                        String value = Context.getSerializationService().getDefaultSerializer().serialize(implementationId);
×
534
                        Context.getAdministrationService().saveGlobalProperty(
×
535
                            new GlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_IMPLEMENTATION_ID, value));
536
                }
537
                catch (APIException e) {
1 ✔
538
                        throw e;
1 ✔
539
                }
540
                catch (Exception e) {
1 ✔
541
                        // pass any other exceptions on up the train
542
                        throw new APIException(e);
1 ✔
543
                }
544
                finally {
1 ✔
545
                        // save an empty concept source to the database when something fails?
546
                }
1 ✔
547
        }
×
548
        
549
        /**
550
         * Checks the remote server for this exact implementation id. Returns the description if 1)
551
         * there is no implementation id or 2) there is a implementation id and this passphrase matches
552
         * it. In the case of 1), this implementation id and passphrase are saved to the remote server's
553
         * database
554
         * 
555
         * @param implementationId
556
         * @param description
557
         * @param passphrase
558
         * @return the stored description on the remote server
559
         * @throws APIException
560
         * @throws UnsupportedEncodingException
561
         */
562
        private String checkImplementationIdValidity(String implementationId, String description, String passphrase)
563
                throws APIException {
564
                
565
                if (StringUtils.isEmpty(implementationId)) {
1 ✔
566
                        throw new APIException("cannot.be.empty", new Object[] { "implementationid" });
1 ✔
567
                }
568
                if (StringUtils.isEmpty(description)) {
1 ✔
569
                        throw new APIException("cannot.be.empty", new Object[] { "description" });
×
570
                }
571
                if (StringUtils.isEmpty(passphrase)) {
1 ✔
572
                        throw new APIException("cannot.be.empty", new Object[] { "passphrase" });
×
573
                }
574
                
575
                // set up the data map to post to the openmrs server
576
                Map<String, String> data = new HashMap<>();
1 ✔
577
                data.put("implementationId", implementationId);
1 ✔
578
                data.put("description", description);
1 ✔
579
                data.put("passphrase", passphrase);
1 ✔
580
                
581
                String response = implementationIdHttpClient.post(data);
1 ✔
582
                response = response.trim();
×
583
                
584
                if ("".equals(response)) {
×
585
                        String ms = Context.getMessageSourceService().getMessage("ImplementationId.connectionError",
×
586
                            new String[] { implementationId }, Context.getLocale());
×
587
                        throw new APIException(ms);
×
588
                }
589
                
590
                log.debug("Response: {}", response);
×
591
                
592
                if (response.startsWith("Success")) {
×
593
                        response = response.replace("Success", "");
×
594
                        return response.trim();
×
595
                }
596
                
597
                String ms = Context.getMessageSourceService().getMessage("ImplementationId.invalidIdorPassphrase",
×
598
                    new String[] { description }, Context.getLocale());
×
599
                throw new APIException(ms);
×
600
        }
601
        
602
        /**
603
         * @see org.openmrs.api.AdministrationService#getAllowedLocales()
604
         */
605
        @Override
606
        @Transactional(readOnly = true)
607
        public List<Locale> getAllowedLocales() {
608
                // lazy-load the global locale list and initialize with current global property value
609
                if (globalLocaleList == null) {
1 ✔
610
                        globalLocaleList = new GlobalLocaleList();
×
611
                        Context.getAdministrationService().addGlobalPropertyListener(globalLocaleList);
×
612
                }
613
                
614
                Set<Locale> allowedLocales = globalLocaleList.getAllowedLocales();
1 ✔
615
                
616
                // update the GlobalLocaleList.allowedLocales by faking a global property change
617
                if (allowedLocales == null) {
1 ✔
618
                        // use a default language of "english" if they have cleared this GP for some reason
619
                        String currentPropertyValue = Context.getAdministrationService().getGlobalProperty(
1 ✔
620
                            OpenmrsConstants.GLOBAL_PROPERTY_LOCALE_ALLOWED_LIST, LocaleUtility.getDefaultLocale().toString());
1 ✔
621
                        GlobalProperty allowedLocalesProperty = new GlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_LOCALE_ALLOWED_LIST,
1 ✔
622
                                currentPropertyValue);
623
                        globalLocaleList.globalPropertyChanged(allowedLocalesProperty);
1 ✔
624
                        allowedLocales = globalLocaleList.getAllowedLocales();
1 ✔
625
                }
626
                
627
                // allowedLocales is guaranteed to not be null at this point
628
                return new ArrayList<>(allowedLocales);
1 ✔
629
        }
630
        
631
        /**
632
         * Used by spring to set the GlobalLocaleList on this implementation
633
         * 
634
         * @param gll the GlobalLocaleList object that is registered to the GlobalPropertyListeners as
635
         *            well
636
         */
637
        public void setGlobalLocaleList(GlobalLocaleList gll) {
638
                globalLocaleList = gll;
1 ✔
639
        }
1 ✔
640
        
641
        /**
642
         * @see org.openmrs.api.AdministrationService#getPresentationLocales()
643
         */
644
        @Override
645
        @Transactional(readOnly = true)
646
        public Set<Locale> getPresentationLocales() {
647
                if (presentationLocales == null) {
1 ✔
648
                        presentationLocales = new LinkedHashSet<>();
1 ✔
649
                        Collection<Locale> messageLocales = Context.getMessageSourceService().getLocales();
1 ✔
650
                        List<Locale> allowedLocales = getAllowedLocales();
1 ✔
651
                        
652
                        for (Locale locale : allowedLocales) {
1 ✔
653
                                // if no country is specified all countries with this language will be added
654
                                if (StringUtils.isEmpty(locale.getCountry())) {
1 ✔
655
                                        List<Locale> localsWithSameLanguage = new ArrayList<>();
1 ✔
656
                                        for (Locale possibleLocale : messageLocales) {
1 ✔
657
                                                if (locale.getLanguage().equals(possibleLocale.getLanguage())
1 ✔
658
                                                        && !StringUtils.isEmpty(possibleLocale.getCountry())) {
1 ✔
659
                                                        localsWithSameLanguage.add(possibleLocale);
1 ✔
660
                                                }
661
                                        }
1 ✔
662
                                        
663
                                        // if there are country locales we add those only
664
                                        if (!localsWithSameLanguage.isEmpty()) {
1 ✔
665
                                                presentationLocales.addAll(localsWithSameLanguage);
1 ✔
666
                                        } else {
667
                                                // if there are no country locales we add possibleLocale which has country as ""
668
                                                // e.g: if 'es' locale has no country based entries es_CL etc. we show default 'es'
669
                                                if (messageLocales.contains(locale)) {
1 ✔
670
                                                        presentationLocales.add(locale);
1 ✔
671
                                                }
672
                                        }
673
                                } else {
1 ✔
674
                                        // if locales list contains exact <language,country> pair add it
675
                                        if (messageLocales.contains(locale)) {
1 ✔
676
                                                presentationLocales.add(locale);
1 ✔
677
                                        } else {
678
                                                // if no such entry add possibleLocale with country ""
679
                                                // e.g: we specify es_CL but it is not in list so we add es locale here
680
                                                for (Locale possibleLocale : messageLocales) {
1 ✔
681
                                                        if (locale.getLanguage().equals(possibleLocale.getLanguage())
1 ✔
682
                                                                && StringUtils.isEmpty(possibleLocale.getCountry())) {
1 ✔
683
                                                                presentationLocales.add(possibleLocale);
1 ✔
684
                                                        }
685
                                                }
1 ✔
686
                                        }
687
                                }
688
                        }
1 ✔
689
                }
690
                return presentationLocales;
1 ✔
691
        }
692
        
693
        /**
694
         * @see org.openmrs.api.GlobalPropertyListener#globalPropertyChanged(org.openmrs.GlobalProperty)
695
         */
696
        @Override
697
        public void globalPropertyChanged(GlobalProperty newValue) {
698
                if (newValue.getProperty().equals(OpenmrsConstants.GLOBAL_PROPERTY_LOCALE_ALLOWED_LIST)) {
1 ✔
699
                        // reset the calculated locale values
700
                        presentationLocales = null;
1 ✔
701
                }
702
        }
1 ✔
703
        
704
        /**
705
         * @see org.openmrs.api.GlobalPropertyListener#globalPropertyDeleted(java.lang.String)
706
         */
707
        @Override
708
        public void globalPropertyDeleted(String propertyName) {
709
                // TODO Auto-generated method stub
710
                
711
        }
1 ✔
712
        
713
        /**
714
         * @see org.openmrs.api.GlobalPropertyListener#supportsPropertyName(java.lang.String)
715
         */
716
        @Override
717
        public boolean supportsPropertyName(String propertyName) {
718
                return propertyName.equals(OpenmrsConstants.GLOBAL_PROPERTY_LOCALE_ALLOWED_LIST);
1 ✔
719
        }
720
        
721
        /**
722
         * @see org.openmrs.api.AdministrationService#getGlobalPropertyByUuid(java.lang.String)
723
         */
724
        @Override
725
        @Transactional(readOnly = true)
726
        public GlobalProperty getGlobalPropertyByUuid(String uuid) {
727
                GlobalProperty gp =  dao.getGlobalPropertyByUuid(uuid);
1 ✔
728
                if (gp == null) {
1 ✔
729
                        return null;
1 ✔
730
                } else if (canViewGlobalProperty(gp)) {
1 ✔
731
                        return gp;
1 ✔
732
                } else {
733
                        throw new APIException("GlobalProperty.error.privilege.required.view", new Object[] {
×
734
                                gp.getViewPrivilege().getPrivilege(), gp.getProperty() });
×
735
                }
736
        }
737
        
738
        /**
739
         * @see org.openmrs.api.AdministrationService#getGlobalPropertyValue(java.lang.String,
740
         *      java.lang.Object)
741
         */
742
        @Override
743
        @SuppressWarnings("unchecked")
744
        @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
745
        public <T> T getGlobalPropertyValue(String propertyName, T defaultValue) throws APIException {
746
                if (defaultValue == null) {
1 ✔
747
                        throw new IllegalArgumentException("The defaultValue argument cannot be null");
1 ✔
748
                }
749
                
750
                String propVal = Context.getAdministrationService().getGlobalProperty(propertyName);
1 ✔
751
                if (StringUtils.isEmpty(propVal)) {
1 ✔
752
                        return defaultValue;
1 ✔
753
                }
754
                
755
                try {
756
                        return (T) defaultValue.getClass().getDeclaredConstructor(String.class).newInstance(propVal);
1 ✔
757
                }
758
                catch (InstantiationException e) {
×
759
                        throw new APIException("is.not.able.instantiated", new Object[] { defaultValue.getClass().getName(), propVal },
×
760
                                e);
761
                }
762
                catch (NoSuchMethodException e) {
×
763
                        throw new APIException("does.not.have.string.constructor", new Object[] { defaultValue.getClass().getName() }, e);
×
764
                }
765
                catch (Exception e) {
×
766
                        log.error("Unable to turn value '" + propVal + "' into type " + defaultValue.getClass().getName(), e);
×
767
                        return defaultValue;
×
768
                }
769
        }
770
        
771
        /**
772
         * @see org.openmrs.api.AdministrationService#getSystemInformation()
773
         */
774
        @Override
775
        @Transactional(readOnly = true)
776
        public Map<String, Map<String, String>> getSystemInformation() throws APIException {
777
                Map<String, Map<String, String>> systemInfoMap = new LinkedHashMap<>();
×
778
                
779
                systemInfoMap.put("SystemInfo.title.openmrsInformation", new LinkedHashMap<String, String>() {
×
780
                        
781
                        private static final long serialVersionUID = 1L;
782
                        
783
                        {
784
                                put("SystemInfo.OpenMRSInstallation.systemDate", new SimpleDateFormat("yyyy-MM-dd").format(Calendar
×
785
                                        .getInstance().getTime()));
×
786
                                put("SystemInfo.OpenMRSInstallation.systemTime", new SimpleDateFormat("HH:mm:ss").format(Calendar
×
787
                                        .getInstance().getTime()));
×
788
                                put("SystemInfo.OpenMRSInstallation.openmrsVersion", OpenmrsConstants.OPENMRS_VERSION);
×
789
                                try {
790
                                        put("SystemInfo.hostname", InetAddress.getLocalHost().getCanonicalHostName());
×
791
                                }
792
                                catch (UnknownHostException e) {
×
793
                                        put("SystemInfo.hostname", "Unknown host: " + e.getMessage());
×
794
                                }
×
795
                        }
×
796
                });
797
                
798
                systemInfoMap.put("SystemInfo.title.javaRuntimeEnvironmentInformation", new LinkedHashMap<String, String>() {
×
799
                        
800
                        Properties properties = System.getProperties();
×
801
                        
802
                        private static final long serialVersionUID = 1L;
803
                        
804
                        {
805
                                put("SystemInfo.JavaRuntimeEnv.operatingSystem", properties.getProperty("os.name"));
×
806
                                put("SystemInfo.JavaRuntimeEnv.operatingSystemArch", properties.getProperty("os.arch"));
×
807
                                put("SystemInfo.JavaRuntimeEnv.operatingSystemVersion", properties.getProperty("os.version"));
×
808
                                put("SystemInfo.JavaRuntimeEnv.javaVersion", properties.getProperty("java.version"));
×
809
                                put("SystemInfo.JavaRuntimeEnv.javaVendor", properties.getProperty("java.vendor"));
×
810
                                put("SystemInfo.JavaRuntimeEnv.jvmVersion", properties.getProperty("java.vm.version"));
×
811
                                put("SystemInfo.JavaRuntimeEnv.jvmVendor", properties.getProperty("java.vm.vendor"));
×
812
                                put("SystemInfo.JavaRuntimeEnv.javaRuntimeName", properties.getProperty("java.runtime.name"));
×
813
                                put("SystemInfo.JavaRuntimeEnv.javaRuntimeVersion", properties.getProperty("java.runtime.version"));
×
814
                                put("SystemInfo.JavaRuntimeEnv.userName", properties.getProperty("user.name"));
×
815
                                put("SystemInfo.JavaRuntimeEnv.systemLanguage", properties.getProperty("user.language"));
×
816
                                put("SystemInfo.JavaRuntimeEnv.systemTimezone", properties.getProperty("user.timezone"));
×
817
                                put("SystemInfo.JavaRuntimeEnv.fileSystemEncoding", properties.getProperty("sun.jnu.encoding"));
×
818
                                put("SystemInfo.JavaRuntimeEnv.userDirectory", properties.getProperty("user.dir"));
×
819
                                put("SystemInfo.JavaRuntimeEnv.tempDirectory", properties.getProperty("java.io.tmpdir"));
×
820
                        }
×
821
                });
822
                
823
                systemInfoMap.put("SystemInfo.title.memoryInformation", new LinkedHashMap<String, String>() {
×
824
                        
825
                        private static final long serialVersionUID = 1L;
826
                        
827
                        Runtime runtime = Runtime.getRuntime();
×
828
                        
829
                        {
830
                                put("SystemInfo.Memory.totalMemory", convertToMegaBytes(runtime.totalMemory()));
×
831
                                put("SystemInfo.Memory.freeMemory", convertToMegaBytes(runtime.freeMemory()));
×
832
                                put("SystemInfo.Memory.maximumHeapSize", convertToMegaBytes(runtime.maxMemory()));
×
833
                                
834
                        }
×
835
                });
836
                
837
                systemInfoMap.put("SystemInfo.title.dataBaseInformation", new LinkedHashMap<String, String>() {
×
838
                        
839
                        Properties properties = Context.getRuntimeProperties();
×
840
                        
841
                        private static final long serialVersionUID = 1L;
842
                        
843
                        {
844
                                put("SystemInfo.Database.name", OpenmrsConstants.DATABASE_NAME);
×
845
                                put("SystemInfo.Database.connectionURL", properties.getProperty("connection.url"));
×
846
                                put("SystemInfo.Database.userName", properties.getProperty("connection.username"));
×
847
                                put("SystemInfo.Database.driver", properties.getProperty("hibernate.connection.driver_class"));
×
848
                                put("SystemInfo.Database.dialect", properties.getProperty("hibernate.dialect"));
×
849
                                
850
                        }
×
851
                });
852
                
853
                systemInfoMap.put("SystemInfo.title.moduleInformation", new LinkedHashMap<String, String>() {
×
854
                        
855
                        private static final long serialVersionUID = 1L;
856
                        
857
                        {
858
                                put("SystemInfo.Module.repositoryPath", ModuleUtil.getModuleRepository().getAbsolutePath());
×
859
                                Collection<Module> loadedModules = ModuleFactory.getLoadedModules();
×
860
                                for (Module module : loadedModules) {
×
861
                                        String moduleInfo = module.getVersion() + " "
×
862
                                                + (module.isStarted() ? "" : Context.getMessageSourceService().getMessage("Module.notStarted"));
×
863
                                        put(module.getName(), moduleInfo);
×
864
                                }
×
865
                        }
×
866
                });
867
                
868
                return systemInfoMap;
×
869
        }
870
        
871
        /**
872
         * @param bytes to be converted into mega bytes
873
         * @return memory in mega bytes
874
         */
875
        private String convertToMegaBytes(long bytes) {
876
                final int ONE_KILO_BYTE = 1024;
×
877
                return String.valueOf(bytes / ONE_KILO_BYTE / ONE_KILO_BYTE) + " MB";
×
878
        }
879
        
880
        /**
881
         * @see org.openmrs.api.AdministrationService#purgeGlobalProperties(java.util.List)
882
         */
883
        @Override
884
        public void purgeGlobalProperties(List<GlobalProperty> globalProperties) throws APIException {
885
                for (GlobalProperty globalProperty : globalProperties) {
1 ✔
886
                        Context.getAdministrationService().purgeGlobalProperty(globalProperty);
1 ✔
887
                }
1 ✔
888
        }
1 ✔
889
        
890
        /**
891
         * @see AdministrationService#getMaximumPropertyLength(Class, String)
892
         */
893
        @Override
894
        @Transactional(readOnly = true)
895
        public int getMaximumPropertyLength(Class<? extends OpenmrsObject> aClass, String fieldName) {
896
                return dao.getMaximumPropertyLength(aClass, fieldName);
1 ✔
897
        }
898
        
899
        /**
900
         * @see org.openmrs.api.AdministrationService#validate(java.lang.Object, Errors)
901
         */
902
        @Override
903
        @Transactional(readOnly = true)
904
        public void validate(Object object, Errors errors) throws APIException {
905
                if (object == null) {
1 ✔
906
                        throw new APIException("error.null", (Object[]) null);
1 ✔
907
                }
908

909
                dao.validate(object, errors);
1 ✔
910
        }
1 ✔
911

912
        @Override
913
        @Cacheable(value = "userSearchLocales")
914
        public List<Locale> getSearchLocales(Locale currentLocale, User user) throws APIException {
915
                Set<Locale> locales = new LinkedHashSet<>();
1 ✔
916
                locales.add(currentLocale); //the currently used full locale
1 ✔
917
                locales.add(new Locale(currentLocale.getLanguage()));
1 ✔
918

919
                if (user != null) {
1 ✔
920
                        List<Locale> proficientLocales = user.getProficientLocales();
1 ✔
921
                        if (proficientLocales != null) {
1 ✔
922
                                locales.addAll(proficientLocales);
1 ✔
923
                        }
924
                }
925
                
926
                //limit locales to only allowed locales
927
                List<Locale> allowedLocales = Context.getAdministrationService().getAllowedLocales();
1 ✔
928
                if (allowedLocales != null) {
1 ✔
929
                        Set<Locale> retainLocales = new HashSet<>();
1 ✔
930
                        
931
                        for (Locale allowedLocale : allowedLocales) {
1 ✔
932
                                retainLocales.add(allowedLocale);
1 ✔
933
                                retainLocales.add(new Locale(allowedLocale.getLanguage()));
1 ✔
934
                        }
1 ✔
935
                        
936
                        locales.retainAll(retainLocales);
1 ✔
937
                }
938
                
939
                return new ArrayList<>(locales);
1 ✔
940
        }
941

942
        /**
943
         * @see AdministrationService#getSearchLocales()
944
         */
945
        @Override
946
        @Transactional(readOnly = true)
947
        public List<Locale> getSearchLocales(){
948
                //call it via interface, so cache interceptor is invoked
949
                return Context.getAdministrationService().getSearchLocales(Context.getLocale(), Context.getAuthenticatedUser());
1 ✔
950
        }
951
        
952
        @Override
953
        public void setImplementationIdHttpClient(HttpClient implementationIdHttpClient) {
954
                this.implementationIdHttpClient = implementationIdHttpClient;
1 ✔
955
        }
1 ✔
956
        
957
        /**
958
         * @see org.openmrs.api.AdministrationService#isDatabaseStringComparisonCaseSensitive()
959
         */
960
        @Override
961
        public boolean isDatabaseStringComparisonCaseSensitive() {
962
                return dao.isDatabaseStringComparisonCaseSensitive();
1 ✔
963
        }
964
        
965
        /**
966
         * @see org.openmrs.api.AdministrationService#updatePostgresSequence()
967
         */
968
        @Override
969
        public void updatePostgresSequence() {
970
                dao.updatePostgresSequence();
×
971
        }
×
972

973
        @Override
974
        public List<String> getSerializerWhitelistTypes() {
975
                List<String> whitelistTypes = new ArrayList<>();
1 ✔
976
                List<Class<?>> hierarchyTypes = getSerializerDefaultWhitelistHierarchyTypes();
1 ✔
977
                for (Class<?> hierarchyType: hierarchyTypes) {
1 ✔
978
                        whitelistTypes.add(GP_SERIALIZER_WHITELIST_HIERARCHY_TYPES_PREFIX + hierarchyType.getName());
1 ✔
979
                }
1 ✔
980

981
                List<GlobalProperty> gpTypes = getGlobalPropertiesBySuffix(
1 ✔
982
                        AdministrationService.GP_SUFFIX_SERIALIZER_WHITELIST_TYPES);
983
                for (GlobalProperty gpType: gpTypes) {
1 ✔
984
                        String[] types = gpType.getPropertyValue().split(",");
1 ✔
985
                        for (String type: types) {
1 ✔
986
                                if(!StringUtils.isBlank(type)) {
1 ✔
987
                                        whitelistTypes.add(type.trim());
1 ✔
988
                                }
989
                        }
990
                }
1 ✔
991

992
                return whitelistTypes;
1 ✔
993
        }
994
        public static List<Class<?>> getSerializerDefaultWhitelistHierarchyTypes() {
995
                List<Class<?>> types = Arrays.asList(OpenmrsObject.class, OpenmrsMetadata.class, OpenmrsData.class, 
1 ✔
996
                        CustomDatatype.class, SingleCustomValue.class, CustomValueDescriptor.class, Customizable.class,
997
                        LayoutTemplate.class, LayoutSupport.class, ComplexData.class, PresentationMessage.class,
998
                        PersonMergeLogData.class);
999
                return types;
1 ✔
1000
        }
1001
        
1002
        /**
1003
         * @see org.openmrs.api.AdministrationService#isCoreSetupOnVersionChangeNeeded() 
1004
         */
1005
        @Override
1006
        public boolean isCoreSetupOnVersionChangeNeeded() {
1007
                if (!Context.isOptimizedStartup()) {
1 ✔
1008
                        return true;
×
1009
                }
1010
                
1011
                String stored = getStoredCoreVersion();
1 ✔
1012
                String current = OpenmrsConstants.OPENMRS_VERSION_SHORT;
1 ✔
1013
                return !Objects.equals(stored, current);
1 ✔
1014
        }
1015

1016
        /**
1017
         * @see org.openmrs.api.AdministrationService#isModuleSetupOnVersionChangeNeeded(String)
1018
         */
1019
        @Override
1020
        public boolean isModuleSetupOnVersionChangeNeeded(String moduleId) {
1021
                Module module = ModuleFactory.getModuleById(moduleId);
1 ✔
1022
                if (module == null) {
1 ✔
1023
                        log.info("{} module is no longer installed, skipping setup", moduleId);
×
1024
                        return false;
×
1025
                }
1026
                
1027
                if (isCoreSetupOnVersionChangeNeeded()) {
1 ✔
1028
                        return true;
1 ✔
1029
                }
1030
                
1031
                String stored = getStoredModuleVersion(moduleId);
×
1032
                String current = module.getVersion();
×
1033
                if (!Objects.equals(stored, current)) {
×
1034
                        return true;
×
1035
                } else {
1036
                        log.info("{} module did not change, skipping setup", moduleId);
×
1037
                        return false;
×
1038
                }
1039
        }
1040

1041
        /**
1042
         * @see org.openmrs.api.AdministrationService#runCoreSetupOnVersionChange() 
1043
         */
1044
        @Override
1045
        @Transactional
1046
        public void runCoreSetupOnVersionChange() throws DatabaseUpdateException {
1047
                if (!ModuleFactory.getLoadedModules().isEmpty()) {
×
1048
                        String prevCoreVersion = getStoredCoreVersion();
×
1049
                        
1050
                        for (Module module : ModuleFactory.getLoadedModules()) {
×
1051
                                String prevModuleVersion = getStoredModuleVersion(module.getModuleId());
×
1052
                                
1053
                                module.getModuleActivator().setupOnVersionChangeBeforeSchemaChanges(prevCoreVersion, prevModuleVersion);
×
1054
                        }
×
1055
                }
1056
                
1057
                boolean updatesRequired;
1058
                try {
1059
                        updatesRequired = DatabaseUpdater.updatesRequired();
×
1060
                }
1061
                catch (Exception e) {
×
1062
                        throw new DatabaseUpdateException("Unable to check if database updates are required", e);
×
1063
                }
×
1064

1065
                // this must be the first thing run in case it changes database mappings
1066
                if (updatesRequired) {
×
1067
                        if (!DatabaseUpdater.allowAutoUpdate()) {
×
1068
                                throw new DatabaseUpdateException(
×
1069
                                        "Database updates are required. Call Context.updateDatabase() before .startup() to continue.");
1070
                        }
1071
                }
1072
                
1073
                DatabaseUpdater.executeChangelog();
×
NEW
1074
                globalPropertyCache.clear();
×
1075
                
1076
                storeCoreVersion();
×
1077
        }
×
1078

1079
        /**
1080
         * @see org.openmrs.api.AdministrationService#runModuleSetupOnVersionChange(Module)
1081
         */
1082
        @Override
1083
        @Transactional
1084
        public void runModuleSetupOnVersionChange(Module module) {
1085
                if (module == null) {
1 ✔
1086
                        return;
×
1087
                }
1088

1089
                String moduleId = module.getModuleId();
1 ✔
1090
                String prevCoreVersion = getStoredCoreVersion();
1 ✔
1091
                String prevModuleVersion = getStoredModuleVersion(moduleId);
1 ✔
1092
                
1093
                ModuleFactory.runLiquibaseForModule(module);
1 ✔
1094
                globalPropertyCache.clear();
1 ✔
1095
                module.getModuleActivator().setupOnVersionChange(prevCoreVersion, prevModuleVersion);
1 ✔
1096
                
1097
                storeModuleVersion(moduleId, module.getVersion());
1 ✔
1098
        }
1 ✔
1099

1100
        protected String getStoredCoreVersion() {
1101
                return dao.getGlobalProperty("core.version");
1 ✔
1102
        }
1103

1104
        protected String getStoredModuleVersion(String moduleId) {
1105
                return dao.getGlobalProperty("module." + moduleId + ".version");
1 ✔
1106
        }
1107

1108
        protected void storeCoreVersion() {
1109
                String propertyName = "core.version";
×
1110
                GlobalProperty gp = new GlobalProperty(propertyName, OpenmrsConstants.OPENMRS_VERSION_SHORT, 
×
1111
                        "Saved core version for future restarts");
1112
                dao.saveGlobalProperty(gp);
×
NEW
1113
                globalPropertyCache.evict(propertyName);
×
UNCOV
1114
        }
×
1115

1116
        protected void storeModuleVersion(String moduleId, String version) {
1117
                String propertyName = "module." + moduleId + ".version";
1 ✔
1118
                GlobalProperty gp = new GlobalProperty(propertyName, version, "Saved module version for future restarts");
1 ✔
1119
                dao.saveGlobalProperty(gp);
1 ✔
1120
                globalPropertyCache.evict(propertyName);
1 ✔
1121
        }
1 ✔
1122
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc