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

openmrs / openmrs-core / 37518343506

06 Oct 2026 07:21PM UTC coverage: 66.431% (-0.03%) from 66.459%
37518343506

push

github

dkayiwa
TRUNK-6819: Start the JobRunr job server only once OpenMRS has started (#6654)

The background job server was started while its application context was
still being created, so jobs could run before the context was ready. On
jetty:run this logged "EntityManagerFactory is closed" for a job that ran
during the context refresh after modules start, against the closed session
factory of the context being replaced, and "Task class ... not found" for a
module's job that ran after the first, core-only refresh, before modules
were loaded.

The server is now started by a SmartLifecycle once its context has finished
refreshing and stopped before the context closes. The webapp defers that
start until SchedulerService.onStartup(), which runs once modules have
started; contexts refreshed after startup start their server right away.

The webapp never closes its application context, so Context.shutdown() now
calls SchedulerService.onShutdown() first, as it did before the move to
JobRunr, and JobRunrSchedulerService stops the job server there, before the
modules and the session factory are shut down.

On 2.9.x, JobRunr 6 does not return the server in its configuration
result, so the lifecycle takes it from JobRunr, and the scheduler service
is declared in applicationContext-service.xml, so its lifecycle is set
there rather than with @Autowired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NK4kFG8qyevx9Bk9jWoEgS
Claude-Session: https://claude.ai/code/session_01PdDdZrB8Q5eMgej5FmRx4R
Claude-Session: https://claude.ai/code/session_01LZ92BDLHrn6xACczVMtSiR

33 of 37 new or added lines in 5 files covered. (89.19%)

38 existing lines in 9 files now uncovered.

25960 of 39078 relevant lines covered (66.43%)

0.66 hits per line

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

55.05
/api/src/main/java/org/openmrs/api/context/Context.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.context;
11

12
import javax.mail.Authenticator;
13
import javax.mail.PasswordAuthentication;
14
import javax.mail.Session;
15
import java.sql.Connection;
16
import java.text.SimpleDateFormat;
17
import java.util.Arrays;
18
import java.util.HashMap;
19
import java.util.HashSet;
20
import java.util.List;
21
import java.util.Locale;
22
import java.util.Map;
23
import java.util.Properties;
24
import java.util.Set;
25
import java.util.concurrent.Future;
26

27
import org.aopalliance.aop.Advice;
28
import org.apache.commons.lang3.StringUtils;
29
import org.openmrs.Allergen;
30
import org.openmrs.GlobalProperty;
31
import org.openmrs.OpenmrsObject;
32
import org.openmrs.PersonName;
33
import org.openmrs.Privilege;
34
import org.openmrs.Role;
35
import org.openmrs.User;
36
import org.openmrs.api.APIException;
37
import org.openmrs.api.AdministrationService;
38
import org.openmrs.api.CohortService;
39
import org.openmrs.api.ConceptService;
40
import org.openmrs.api.ConditionService;
41
import org.openmrs.api.DatatypeService;
42
import org.openmrs.api.DiagnosisService;
43
import org.openmrs.api.EncounterService;
44
import org.openmrs.api.FormService;
45
import org.openmrs.api.LocationService;
46
import org.openmrs.api.MedicationDispenseService;
47
import org.openmrs.api.ObsService;
48
import org.openmrs.api.OpenmrsService;
49
import org.openmrs.api.OrderService;
50
import org.openmrs.api.OrderSetService;
51
import org.openmrs.api.PatientService;
52
import org.openmrs.api.PersonService;
53
import org.openmrs.api.ProgramWorkflowService;
54
import org.openmrs.api.ProviderService;
55
import org.openmrs.api.SerializationService;
56
import org.openmrs.api.UserService;
57
import org.openmrs.api.VisitService;
58
import org.openmrs.api.db.ContextDAO;
59
import org.openmrs.hl7.HL7Service;
60
import org.openmrs.logic.LogicService;
61
import org.openmrs.messagesource.MessageSourceService;
62
import org.openmrs.module.ModuleMustStartException;
63
import org.openmrs.module.ModuleUtil;
64
import org.openmrs.notification.AlertService;
65
import org.openmrs.notification.MessageException;
66
import org.openmrs.notification.MessagePreparator;
67
import org.openmrs.notification.MessageSender;
68
import org.openmrs.notification.MessageService;
69
import org.openmrs.notification.mail.MailMessageSender;
70
import org.openmrs.notification.mail.velocity.VelocityMessagePreparator;
71
import org.openmrs.scheduler.SchedulerService;
72
import org.openmrs.util.ConfigUtil;
73
import org.openmrs.util.DatabaseUpdateException;
74
import org.openmrs.util.DatabaseUpdater;
75
import org.openmrs.util.InputRequiredException;
76
import org.openmrs.util.LocaleUtility;
77
import org.openmrs.util.OpenmrsClassLoader;
78
import org.openmrs.util.OpenmrsConstants;
79
import org.openmrs.util.OpenmrsUtil;
80
import org.openmrs.util.PrivilegeConstants;
81
import org.openmrs.validator.ValidateUtil;
82
import org.slf4j.Logger;
83
import org.slf4j.LoggerFactory;
84
import org.springframework.aop.Advisor;
85
import org.springframework.beans.BeansException;
86
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
87
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
88

89
/**
90
 * Represents an OpenMRS <code>Context</code>, which may be used to authenticate to the database and
91
 * obtain services in order to interact with the system.<br>
92
 * <br>
93
 * The Context is split into a {@link UserContext} and {@link ServiceContext}. The UserContext is
94
 * lightweight and there is an instance for every user logged into the system. The ServiceContext is
95
 * heavier and it contains each service class. This is more static and there is only one ServiceContext
96
 * per OpenMRS instance. <br>
97
 * <br>
98
 * Both the {@link UserContext} and the {@link ServiceContext} should not be used directly. This
99
 * context class has methods to pass through to the currently defined UserContext for the thread and
100
 * the currently defined ServiceContext. <br>
101
 * <br>
102
 * To use the OpenMRS api there are four things that have to be done:
103
 * <ol>
104
 * <li>Call {@link Context#startup(String, String, String, Properties)} to let the Context contact
105
 * the database</li>
106
 * <li>Call {@link Context#openSession()} to start a "unit of work".</li>
107
 * <li>Call {@link Context#authenticate(String, String)} to authenticate the current user on the
108
 * current thread</li>
109
 * <li>Call {@link Context#closeSession()} to end your "unit of work" and commit all changes to the
110
 * database.</li>
111
 * </ol>
112
 * <br>
113
 * Example usage:
114
 *
115
 * <pre>
116
 *         public static void main(String[] args) {
117
 *                 Context.startup("jdbc:mysql://localhost:3306/db-name?autoReconnect=true", "openmrs-db-user", "3jknfjkn33ijt", new Properties());
118
 *                 try {
119
 *                         Context.openSession();
120
 *                         Context.authenticate("admin", "test");
121
 *                         List&lt;Patients&gt; patients = Context.getPatientService().getPatientsByName("Fred");
122
 *                         patients.get(0).setBirthdate(new Date());
123
 *                         Context.getPatientService().savePatient(patients.get(0));
124
 *                         ...
125
 *        }
126
 *                 finally {
127
 *                         Context.closeSession();
128
 *        }
129
 *    }
130
 * </pre>
131
 *
132
 * @see org.openmrs.api.context.UserContext
133
 * @see org.openmrs.api.context.ServiceContext
134
 */
135
public class Context {
136

137

138
        // Global resources
139
        private static ContextDAO contextDAO;
140

141
        private static volatile Session mailSession;
142

143
        // Using "wrapper" (Object array) around UserContext to avoid ThreadLocal
144
        // bug in Java 1.5
145
        private static final ThreadLocal<Object[] /* UserContext */> userContextHolder = new ThreadLocal<>();
1 ✔
146

147
        private static volatile ServiceContext serviceContext;
148

149
        private static Properties runtimeProperties = new Properties();
1 ✔
150

151
        private static Properties configProperties = new Properties();
1 ✔
152

153
        private static AuthenticationScheme authenticationScheme;
154

155
        private static final Logger log = LoggerFactory.getLogger(Context.class);
1 ✔
156

157
        /**
158
         * Default public constructor
159
         */
160
        public Context() {
1 ✔
161
        }
1 ✔
162

163
        /**
164
         * Gets the context's data access object
165
         *
166
         * @return ContextDAO
167
         */
168
        static ContextDAO getContextDAO() {
169
                if (contextDAO == null) {
1 ✔
170
                        throw new APIException("error.context.null", (Object[]) null);
×
171
                }
172
                return contextDAO;
1 ✔
173
        }
174

175
        /**
176
         * Used to set the context's DAO for the application.
177
         *
178
         * @param dao ContextDAO to set
179
         */
180
        public void setContextDAO(ContextDAO dao) {
181
                setDAO(dao);
1 ✔
182
        }
1 ✔
183

184
        public static void setDAO(ContextDAO dao) {
185
                contextDAO = dao;
1 ✔
186
        }
1 ✔
187

188
        /**
189
         * Spring init method that sets the authentication scheme.
190
         */
191
        private static void setAuthenticationScheme() {
192

193
                authenticationScheme = new UsernamePasswordAuthenticationScheme();
1 ✔
194

195
                try {
196
                        authenticationScheme = Context.getServiceContext().getApplicationContext().getBean(AuthenticationScheme.class); // manual autowiring (from a module)
×
197
                        log.info("An authentication scheme override was provided. Using this one in place of the OpenMRS default authentication scheme.");
×
198
                }
199
                catch(NoUniqueBeanDefinitionException e) {
×
200
                        log.error("Multiple authentication schemes overrides are being provided, this is currently not supported. Sticking to OpenMRS default authentication scheme.");
×
201
                }
202
                catch(NoSuchBeanDefinitionException e) {
1 ✔
203
                        log.debug("No authentication scheme override was provided. Sticking to OpenMRS default authentication scheme.");
1 ✔
204
                }
205
                catch(BeansException e){
×
206
                        log.error("Fatal error encountered when injecting the authentication scheme override. Sticking to OpenMRS default authentication scheme.");
×
207
                }
1 ✔
208
        }
1 ✔
209

210
        /**
211
         * Loads a class with an instance of the OpenmrsClassLoader. Convenience method equivalent to
212
         * OpenmrsClassLoader.getInstance().loadClass(className);
213
         *
214
         * @param className the class to load
215
         * @return the class that was loaded
216
         * @throws ClassNotFoundException
217
         * <strong>Should</strong> load class with the OpenmrsClassLoader
218
         */
219
        public static Class<?> loadClass(String className) throws ClassNotFoundException {
220
                return OpenmrsClassLoader.getInstance().loadClass(className);
1 ✔
221
        }
222

223
        /**
224
         * Sets the user context on the thread local so that the service layer can perform
225
         * authentication/authorization checks.<br>
226
         * <br>
227
         * This is thread safe since it stores the given user context in ThreadLocal.
228
         *
229
         * @param ctx UserContext to set
230
         */
231
        public static void setUserContext(UserContext ctx) {
232
                log.trace("Setting user context {}", ctx);
1 ✔
233

234
                Object[] arr = new Object[] { ctx };
1 ✔
235
                userContextHolder.set(arr);
1 ✔
236
        }
1 ✔
237

238
        /**
239
         * Clears the user context from the threadlocal.
240
         */
241
        public static void clearUserContext() {
242
                log.trace("Clearing user context {}", Arrays.toString(userContextHolder.get()));
1 ✔
243

244
                userContextHolder.remove();
1 ✔
245
        }
1 ✔
246

247
        /**
248
         * Gets the user context from the thread local. This might be accessed by several threads at the
249
         * same time.
250
         *
251
         * @return The current UserContext for this thread.
252
         * <strong>Should</strong> fail if session hasn't been opened
253
         */
254
        public static UserContext getUserContext() {
255
                Object[] arr = userContextHolder.get();
1 ✔
256
                log.trace("Getting user context {} from userContextHolder {}", Arrays.toString(arr), userContextHolder);
1 ✔
257

258
                if (arr == null) {
1 ✔
259
                        log.trace("userContext is null.");
1 ✔
260
                        throw new APIException(
1 ✔
261
                                        "A user context must first be passed to setUserContext()...use Context.openSession() (and closeSession() to prevent memory leaks!) before using the API");
262
                }
263
                return (UserContext) userContextHolder.get()[0];
1 ✔
264
        }
265

266
        /**
267
         * Gets the currently defined service context. If one is not defined, one will be created and
268
         * then returned.
269
         *
270
         * @return the current ServiceContext
271
         */
272
        static ServiceContext getServiceContext() {
273
                if (serviceContext == null) {
1 ✔
274
                        synchronized (Context.class) {
1 ✔
275
                                if (serviceContext == null) {
1 ✔
276
                                        serviceContext = ServiceContext.getInstance();
1 ✔
277
                                }
278
                        }
1 ✔
279
                }
280
                return ServiceContext.getInstance();
1 ✔
281
        }
282

283
        /**
284
         * Sets the service context.
285
         *
286
         * @param ctx
287
         */
288
        public void setServiceContext(ServiceContext ctx) {
289
                setContext(ctx);
1 ✔
290
        }
1 ✔
291

292
        public static void setContext(ServiceContext ctx) {
293
                serviceContext = ctx;
1 ✔
294
        }
1 ✔
295

296
        /**
297
         * OpenMRS provides its default authentication scheme that authenticates via DAO with OpenMRS usernames and passwords.
298
         * 
299
         * Any module can provide an authentication scheme override by Spring wiring a custom implementation of {@link AuthenticationScheme}.
300
         * This method would return Core's default authentication scheme unless a Spring override is provided somewhere else.
301
         * 
302
         * @return The enforced authentication scheme.
303
         */
304
        public static AuthenticationScheme getAuthenticationScheme() {
305
                return authenticationScheme;
1 ✔
306
        }
307

308
        /**
309
         * @deprecated as of 2.3.0, replaced by {@link #authenticate(Credentials)}
310
         * 
311
         * Used to authenticate user within the context
312
         *
313
         * @param username user's identifier token for login
314
         * @param password user's password for authenticating to context
315
         * @throws ContextAuthenticationException
316
         * <strong>Should</strong> not authenticate with null username and password
317
         * <strong>Should</strong> not authenticate with null password
318
         * <strong>Should</strong> not authenticate with null username
319
         * <strong>Should</strong> not authenticate with null password and proper username
320
         * <strong>Should</strong> not authenticate with null password and proper system id
321
         */
322
        @Deprecated
323
        public static void authenticate(String username, String password) throws ContextAuthenticationException {
324
                authenticate(new UsernamePasswordCredentials(username, password));
1 ✔
325
        }
1 ✔
326

327
        /**
328
         * @param credentials
329
         * @throws ContextAuthenticationException
330
         * 
331
         * @since 2.3.0
332
         */
333
        public static Authenticated authenticate(Credentials credentials) throws ContextAuthenticationException {
334

335
                if (Daemon.isDaemonThread()) {
1 ✔
336
                        log.error("Authentication attempted while operating on a "
×
337
                                        + "daemon thread, authenticating is not necessary or allowed");
338
                        return new BasicAuthenticated(Daemon.getDaemonThreadUser(), "No auth scheme used by Context - Daemon user is always authenticated.");
×
339
                }
340

341
                if (credentials == null) {
1 ✔
342
                        throw new ContextAuthenticationException("Context cannot authenticate with null credentials.");
×
343
                }
344

345
                return getUserContext().authenticate(credentials);
1 ✔
346
        }
347

348
        /**
349
         * Refresh the authenticated user object in the current UserContext. This should be used when
350
         * updating information in the database about the current user and it needs to be reflecting in
351
         * the (cached) {@link #getAuthenticatedUser()} User object.
352
         *
353
         * @since 1.5
354
         * <strong>Should</strong> get fresh values from the database
355
         */
356
        public static void refreshAuthenticatedUser() {
357
                if (Daemon.isDaemonThread()) {
1 ✔
358
                        return;
×
359
                }
360
                log.debug("Refreshing authenticated user");
1 ✔
361

362
                getUserContext().refreshAuthenticatedUser();
1 ✔
363
        }
1 ✔
364

365
        /**
366
         * Become a different user. (You should only be able to do this as a superuser.)
367
         *
368
         * @param systemId
369
         * @throws ContextAuthenticationException
370
         * <strong>Should</strong> change locale when become another user
371
         */
372
        public static void becomeUser(String systemId) throws ContextAuthenticationException {
373
                log.info("systemId: {}", systemId);
1 ✔
374

375
                getUserContext().becomeUser(systemId);
1 ✔
376
        }
1 ✔
377

378
        /**
379
         * Get the runtime properties that this OpenMRS instance was started with
380
         *
381
         * @return copy of the runtime properties
382
         */
383
        public static Properties getRuntimeProperties() {
384
                Properties props = new Properties();
1 ✔
385
                if( runtimeProperties != null ) {
1 ✔
386
                        props.putAll(runtimeProperties);
1 ✔
387
                }
388
                return props;
1 ✔
389
        }
390

391
        /**
392
         * Set the runtime properties to be used by this OpenMRS instance
393
         *
394
         * @param props runtime properties
395
         */
396
        public static void setRuntimeProperties(Properties props) {
397
                runtimeProperties = props;
1 ✔
398
        }
1 ✔
399

400
        /**
401
         * @return concept dictionary-related services
402
         */
403
        public static ConceptService getConceptService() {
404
                return getServiceContext().getConceptService();
1 ✔
405
        }
406

407
        /**
408
         * @return encounter-related services
409
         */
410
        public static EncounterService getEncounterService() {
411
                return getServiceContext().getEncounterService();
1 ✔
412
        }
413

414
        /**
415
         * @return location services
416
         */
417
        public static LocationService getLocationService() {
418
                return getServiceContext().getLocationService();
1 ✔
419
        }
420

421
        /**
422
         * @return observation services
423
         */
424
        public static ObsService getObsService() {
425
                return getServiceContext().getObsService();
1 ✔
426
        }
427

428
        /**
429
         * @return patient-related services
430
         */
431
        public static PatientService getPatientService() {
432
                return getServiceContext().getPatientService();
1 ✔
433
        }
434

435
        public static CohortService getCohortService() {
436
                return getServiceContext().getCohortService();
1 ✔
437
        }
438

439
        /**
440
         * @return person-related services
441
         */
442
        public static PersonService getPersonService() {
443
                return getServiceContext().getPersonService();
1 ✔
444
        }
445

446
        /**
447
         * @return condition-related services
448
         * 
449
         * @since 2.2
450
         */
451
        public static ConditionService getConditionService(){
452
                return getServiceContext().getConditionService();
1 ✔
453
        }
454

455
        /**
456
         * @return diagnosis-related services
457
         *
458
         * @since 2.2
459
         */
460
        public static DiagnosisService getDiagnosisService(){
461
                return getServiceContext().getDiagnosisService();
1 ✔
462
        }
463

464
        /**
465
         * @return MedicationDispense-related service
466
         * @since 2.6.0
467
         */
468
        public static MedicationDispenseService getMedicationDispenseService(){
469
                return getServiceContext().getMedicationDispenseService();
1 ✔
470
        }
471

472
        /**
473
         * @return Returns the hl7Service.
474
         */
475
        public static HL7Service getHL7Service() {
476
                return getServiceContext().getHL7Service();
1 ✔
477
        }
478

479
        /**
480
         * @return user-related services
481
         */
482
        public static UserService getUserService() {
483
                return getServiceContext().getUserService();
1 ✔
484
        }
485

486
        /**
487
         * @return order service
488
         */
489
        public static OrderService getOrderService() {
490
                return getServiceContext().getOrderService();
1 ✔
491
        }
492

493
        /**
494
         * @return orderSet service
495
         * @since 1.12
496
         */
497
        public static OrderSetService getOrderSetService() {
498
                return getServiceContext().getOrderSetService();
1 ✔
499
        }
500

501
        /**
502
         * @return form service
503
         */
504
        public static FormService getFormService() {
505
                return getServiceContext().getFormService();
1 ✔
506
        }
507

508
        /**
509
         * @return serialization service
510
         * @since 1.5
511
         */
512
        public static SerializationService getSerializationService() {
513
                return getServiceContext().getSerializationService();
1 ✔
514
        }
515

516
        /**
517
         * @return logic service
518
         */
519
        public static LogicService getLogicService() {
520
                return getServiceContext().getLogicService();
×
521
        }
522

523
        /**
524
         * @return admin-related services
525
         */
526
        public static AdministrationService getAdministrationService() {
527
                return getServiceContext().getAdministrationService();
1 ✔
528
        }
529

530
        /**
531
         * @return MessageSourceService
532
         */
533
        public static MessageSourceService getMessageSourceService() {
534
                return getServiceContext().getMessageSourceService();
1 ✔
535
        }
536

537
        /**
538
         * @return scheduler service
539
         */
540
        public static SchedulerService getSchedulerService() {
541
                return getServiceContext().getSchedulerService();
1 ✔
542
        }
543

544
        /**
545
         * @return alert service
546
         */
547
        public static AlertService getAlertService() {
548
                return getServiceContext().getAlertService();
1 ✔
549
        }
550

551
        /**
552
         * @return program- and workflow-related services
553
         */
554
        public static ProgramWorkflowService getProgramWorkflowService() {
555
                return getServiceContext().getProgramWorkflowService();
1 ✔
556
        }
557
        
558
        /**
559
         * Get the message service.
560
         *
561
         * @return message service
562
         */
563
        public static MessageService getMessageService() {
564
                MessageService ms = getServiceContext().getMessageService();
1 ✔
565
                try {
566
                        // Message service dependencies
567
                        if (ms.getMessagePreparator() == null) {
1 ✔
568
                                ms.setMessagePreparator(getMessagePreparator());
1 ✔
569
                        }
570

571
                        if (ms.getMessageSender() == null) {
1 ✔
572
                                ms.setMessageSender(getMessageSender());
1 ✔
573
                        }
574

575
                }
576
                catch (Exception e) {
×
577
                        log.error("Unable to create message service due", e);
×
578
                }
1 ✔
579
                return ms;
1 ✔
580
        }
581

582
        /**
583
         * @return all of the configured properties that are used to configure the Mail Session in the Message Service
584
         * These properties are defined as all properties that are prefixed with "mail." and this will return all such
585
         * properties as defined in global properties, runtime properties, and/or system properties, with 
586
         * system properties overriding runtime properties overriding global properties.
587
         */
588
        public static Properties getMailProperties() {
589
                Properties p = new Properties();
1 ✔
590
                String prefix = "mail.";
1 ✔
591
                for (GlobalProperty gp : getAdministrationService().getGlobalPropertiesByPrefix(prefix)) {
1 ✔
592
                        // Historically, some mail properties defined with underscores, support these for legacy compatibility
593
                        if (gp.getProperty().equals("mail.transport_protocol")) {
×
594
                                p.setProperty("mail.transport.protocol", gp.getPropertyValue());
×
595
                        }
596
                        else if (gp.getProperty().equals("mail.smtp_host")) {
×
597
                                p.setProperty("mail.smtp.host", gp.getPropertyValue());
×
598
                        }
599
                        else if (gp.getProperty().equals("mail.smtp_port")) {
×
600
                                p.setProperty("mail.smtp.port", gp.getPropertyValue());
×
601
                        }
602
                        else if (gp.getProperty().equals("mail.smtp_auth")) {
×
603
                                p.setProperty("mail.smtp.auth", gp.getPropertyValue());
×
604
                        }
605
                        else {
606
                                p.setProperty(gp.getProperty(), gp.getPropertyValue());
×
607
                        }
608
                }
×
609
                for (String runtimeProperty : runtimeProperties.stringPropertyNames()) {
1 ✔
610
                        if (runtimeProperty.startsWith(prefix)) {
1 ✔
611
                                p.setProperty(runtimeProperty, runtimeProperties.getProperty(runtimeProperty));
×
612
                        }
613
                }
1 ✔
614
                for (String systemProperty : System.getProperties().stringPropertyNames()) {
1 ✔
615
                        if (systemProperty.startsWith(prefix)) {
1 ✔
616
                                p.setProperty(systemProperty, System.getProperty(systemProperty));
×
617
                        }
618
                }
1 ✔
619
                return p;
1 ✔
620
        }
621

622
        /**
623
         * Gets the mail session required by the mail message service. This function forces
624
         * authentication via the getAdministrationService() method call
625
         *
626
         * @return a java mail session
627
         */
628
        private static Session getMailSession() {
629
                if (mailSession == null) {
1 ✔
630
                        synchronized (Context.class) {
1 ✔
631
                                if (mailSession == null) {
1 ✔
632
                                        Authenticator auth = new Authenticator() {
1 ✔
633

634
                                                @Override
635
                                                public PasswordAuthentication getPasswordAuthentication() {
636
                                                        return new PasswordAuthentication(
×
637
                                                                ConfigUtil.getProperty("mail.user"),
×
638
                                                                ConfigUtil.getProperty("mail.password")
×
639
                                                        );
640
                                                }
641
                                        };
642
                                        mailSession = Session.getInstance(getMailProperties(), auth);
1 ✔
643
                                }
644
                        }
1 ✔
645
                }
646
                return mailSession;
1 ✔
647
        }
648

649
        /**
650
         * Convenience method to allow us to change the configuration more easily. TODO Ideally, we
651
         * would be using Spring's method injection to set the dependencies for the message service.
652
         *
653
         * @return the ServiceContext
654
         */
655
        private static MessageSender getMessageSender() {
656
                return new MailMessageSender(getMailSession());
1 ✔
657
        }
658

659
        /**
660
         * Convenience method to allow us to change the configuration more easily. TODO See todo for
661
         * message sender.
662
         *
663
         * @return
664
         */
665
        private static MessagePreparator getMessagePreparator() throws MessageException {
666
                return new VelocityMessagePreparator();
1 ✔
667
        }
668

669
        /**
670
         * @return "active" user who has been authenticated, otherwise <code>null</code>
671
         */
672
        public static User getAuthenticatedUser() {
673
                if (Daemon.isDaemonThread()) {
1 ✔
674
                        return Daemon.getDaemonThreadUser();
1 ✔
675
                }
676

677
                return getUserContext().getAuthenticatedUser();
1 ✔
678
        }
679

680
        /**
681
         * @return true if user has been authenticated in this context
682
         */
683
        public static boolean isAuthenticated() {
684
                if (Daemon.isDaemonThread()) {
1 ✔
685
                        return true;
×
686
                } else {
687
                        try {
688
                                return getAuthenticatedUser() != null;
1 ✔
689
                        } catch (APIException e) {
×
690
                                log.info("Could not get authenticated user inside called to isAuthenticated(), assuming no user context has been defined", e);
×
691
                                return false;
×
692
                        }
693
                }
694
        }
695

696
        /**
697
         * Used by {@link org.openmrs.aop.AuthorizationAdvice} to give access for unauthenticated users when
698
         * running initialization code with proxy privileges. See e.g.
699
         * {@link org.openmrs.event.outbox.tasks.OutboxTaskSchedulerInitializer#schedule()}.
700
         *
701
         * @return true if there are any proxy privileges
702
         * @since 2.9.0
703
         */
704
        public static boolean hasProxyPrivileges() {
705
                return getUserContext().hasProxyPrivileges();
1 ✔
706
        }
707

708
        /**
709
         * logs out the "active" (authenticated) user within context
710
         *
711
         * @see #authenticate
712
         * <strong>Should</strong> not fail if session hasn't been opened yet
713
         */
714
        public static void logout() {
715
                if (!isSessionOpen()) {
1 ✔
716
                        return; // fail early if there isn't even a session open
1 ✔
717
                }
718
                log.debug("Logging out : {}", getAuthenticatedUser());
1 ✔
719

720
                getUserContext().logout();
1 ✔
721

722
                // reset the UserContext object (usually cleared out by closeSession()
723
                // soon after this)
724
                setUserContext(new UserContext(getAuthenticationScheme()));
1 ✔
725
        }
1 ✔
726

727
        /**
728
         * Convenience method. Passes through to userContext.getAllRoles(User)
729
         */
730
        public static Set<Role> getAllRoles(User user) throws Exception {
731
                return getUserContext().getAllRoles();
×
732
        }
733

734
        /**
735
         * Tests whether the currently authenticated user has a particular privilege
736
         */
737
        public static boolean hasPrivilege(String privilege) {
738
                // the daemon threads have access to all things
739
                if (Daemon.isDaemonThread()) {
1 ✔
740
                        return true;
1 ✔
741
                }
742

743
                return getUserContext().hasPrivilege(privilege);
1 ✔
744
        }
745

746
        /**
747
         * Tests whether the currently authenticated user has a particular privilege, optionally excluding
748
         * proxy privileges. Passing <code>false</code> checks only the user's granted roles (and the
749
         * anonymous and authenticated roles), so a proxy privilege added merely to invoke a service method
750
         * cannot satisfy a per-resource access check.
751
         *
752
         * @param privilege the privilege to check
753
         * @param includeProxyPrivileges whether proxy privileges may satisfy the check
754
         * @return true if the current user has the given privilege
755
         * @see UserContext#hasPrivilege(String, boolean)
756
         * @since 3.0.0, 2.9.0, 2.8.9
757
         */
758
        public static boolean hasPrivilege(String privilege, boolean includeProxyPrivileges) {
759
                // the daemon threads have access to all things
760
                if (Daemon.isDaemonThread()) {
1 ✔
761
                        return true;
×
762
                }
763

764
                return getUserContext().hasPrivilege(privilege, includeProxyPrivileges);
1 ✔
765
        }
766

767
        /**
768
         * Throws an exception if the currently authenticated user does not have the specified
769
         * privilege.
770
         *
771
         * @param privilege
772
         * @throws ContextAuthenticationException
773
         */
774
        public static void requirePrivilege(String privilege) throws ContextAuthenticationException {
775
                if (!hasPrivilege(privilege)) {
1 ✔
776
                        String errorMessage;
777
                        if (StringUtils.isNotBlank(privilege)) {
×
778
                                errorMessage = Context.getMessageSourceService().getMessage("error.privilegesRequired",
×
779
                                                new Object[] { privilege }, null);
780
                        } else {
781
                                //Should we even be here if the privilege is blank?
782
                                errorMessage = Context.getMessageSourceService().getMessage("error.privilegesRequiredNoArgs");
×
783
                        }
784

785
                        throw new ContextAuthenticationException(errorMessage);
×
786
                }
787
        }
1 ✔
788
        
789
        /**
790
         * Adds one or more privileges to the list of privileges that that {@link #hasPrivilege(String)} will
791
         * regard as available regardless of whether the user would otherwise have the privilege.
792
         * <p/>
793
         * This is useful for situations where a system process may need access to some piece of data that the
794
         * user would not otherwise have access to, like a GlobalProperty. <strong>This facility should not be
795
         * used to return data to the user that they otherwise would be unable to see.</strong>
796
         * <p/>
797
         * The expected usage is:
798
         * <p/>
799
         * <pre>{@code
800
         * try {
801
         *   Context.addProxyPrivilege(&quot;AAA&quot;);
802
         *   Context.get*Service().methodRequiringAAAPrivilege();
803
         * }
804
         * finally {
805
         *   Context.removeProxyPrivilege(&quot;AAA&quot;);
806
         * }}
807
         * </pre>
808
         * <p/>
809
         *
810
         * @param privilege privileges to add in string form
811
         * @see #hasPrivilege(String)
812
         * @see #removeProxyPrivilege(String)
813
         */
814
        public static void addProxyPrivilege(String privilege) {
815
                getUserContext().addProxyPrivilege(privilege);
1 ✔
816
        }
1 ✔
817
        
818
        /**
819
         * Adds one or more privileges to the list of privileges that that {@link #hasPrivilege(String)} will
820
         * regard as available regardless of whether the user would otherwise have the privilege.
821
         * <p/>
822
         * This is useful for situations where a system process may need access to some piece of data that the
823
         * user would not otherwise have access to, like a GlobalProperty. <strong>This facility should not be
824
         * used to return data to the user that they otherwise would be unable to see.</strong>
825
         * <p/>
826
         * The expected usage is:
827
         * <p/>
828
         * <pre>{@code
829
         * try {
830
         *   Context.addProxyPrivilege(&quot;AAA&quot;);
831
         *   Context.get*Service().methodRequiringAAAPrivilege();
832
         * }
833
         * finally {
834
         *   Context.removeProxyPrivilege(&quot;AAA&quot;);
835
         * }}
836
         * </pre>
837
         * <p/>
838
         *
839
         * @param privileges privileges to add in string form
840
         * @see #hasPrivilege(String)
841
         * @see #removeProxyPrivilege(String...)
842
         * @since 3.0.0, 2.8.2, 2.7.8
843
         */
844
        public static void addProxyPrivilege(String... privileges) {
845
                getUserContext().addProxyPrivilege(privileges);
1 ✔
846
        }
1 ✔
847

848
        /**
849
         * Removes one or more privileges from the list of privileges that that {@link #hasPrivilege(String)} will
850
         * regard as available regardless of whether the user would otherwise have the privilege.
851
         * <p/>
852
         * This is the compliment for {@link #addProxyPrivilege(String...)} to clean-up the context.
853
         * <p/>
854
         *
855
         * @param privilege privileges to remove in string form
856
         * @see #hasPrivilege(String)
857
         * @see #addProxyPrivilege(String)
858
         */
859
        public static void removeProxyPrivilege(String privilege) {
860
                getUserContext().removeProxyPrivilege(privilege);
1 ✔
861
        }
1 ✔
862

863
        /**
864
         * Removes a privilege from the list of privileges that that {@link #hasPrivilege(String)} will
865
         * regard as available regardless of whether the user would otherwise have the privilege.
866
         * <p/>
867
         * This is the compliment for {@link #addProxyPrivilege(String...)} to clean-up the context.
868
         * <p/>
869
         *
870
         * @param privileges privileges to remove in string form
871
         * @see #hasPrivilege(String)
872
         * @see #addProxyPrivilege(String...)
873
         * * @since 3.0.0, 2.8.2, 2.7.8
874
         */
875
        public static void removeProxyPrivilege(String... privileges) {
876
                getUserContext().removeProxyPrivilege(privileges);
1 ✔
877
        }
1 ✔
878

879
        /**
880
         * Convenience method. Passes through to {@link UserContext#setLocale(Locale)}
881
         */
882
        public static void setLocale(Locale locale) {
883
                getUserContext().setLocale(locale);
1 ✔
884
        }
1 ✔
885

886
        /**
887
         * Convenience method. Passes through to {@link UserContext#getLocale()}
888
         *
889
         * <strong>Should</strong> not fail if session hasn't been opened
890
         */
891
        public static Locale getLocale() {
892
                // if a session hasn't been opened, just fetch the default
893
                if (!isSessionOpen()) {
1 ✔
894
                        return LocaleUtility.getDefaultLocale();
1 ✔
895
                }
896

897
                return getUserContext().getLocale();
1 ✔
898
        }
899

900
        /**
901
         * Used to define a unit of work. All "units of work" should be surrounded by openSession and
902
         * closeSession calls.
903
         */
904
        public static void openSession() {
905
                log.trace("opening session");
1 ✔
906
                setUserContext(new UserContext(getAuthenticationScheme())); // must be cleared out in
1 ✔
907
                // closeSession()
908
                getContextDAO().openSession();
1 ✔
909
        }
1 ✔
910

911
        /**
912
         * Used to define a unit of work. All "units of work" should be surrounded by openSession and
913
         * closeSession calls.
914
         */
915
        public static void closeSession() {
916
                log.trace("closing session");
1 ✔
917
                clearUserContext(); // because we set a UserContext on the current
1 ✔
918
                // thread in openSession()
919
                getContextDAO().closeSession();
1 ✔
920
        }
1 ✔
921

922
        /**
923
         * Used to define a unit of work which does not require clearing out the currently authenticated
924
         * user. Remember to call closeSessionWithCurrentUser in a, preferably, finally block after this
925
         * work.
926
         *
927
         * @since 1.10
928
         */
929
        public static void openSessionWithCurrentUser() {
930
                getContextDAO().openSession();
×
931
        }
×
932

933
        /**
934
         * Used when the a unit of work which started with a call for openSessionWithCurrentUser has
935
         * finished. This should be in a, preferably, finally block.
936
         *
937
         * @since 1.10
938
         */
939
        public static void closeSessionWithCurrentUser() {
940
                getContextDAO().closeSession();
×
941
        }
×
942

943
        /**
944
         * Clears cached changes made so far during this unit of work without writing them to the
945
         * database. If you call this method, and later call closeSession() or flushSession() your
946
         * changes are still lost.
947
         */
948
        public static void clearSession() {
949
                log.trace("clearing session");
1 ✔
950
                getContextDAO().clearSession();
1 ✔
951
        }
1 ✔
952

953
        /**
954
         * Forces any changes made so far in this unit of work to be written to the database
955
         *
956
         * @since 1.6
957
         */
958
        public static void flushSession() {
959
                log.trace("flushing session");
1 ✔
960
                getContextDAO().flushSession();
1 ✔
961
        }
1 ✔
962

963
        /**
964
         * This method tells whether {@link #openSession()} has been called or not already. If it hasn't
965
         * been called, some methods won't work correctly because a {@link UserContext} isn't available.
966
         *
967
         * @return true if {@link #openSession()} has been called already.
968
         * @since 1.5
969
         * <strong>Should</strong> return true if session is closed
970
         */
971
        public static boolean isSessionOpen() {
972
                return userContextHolder.get() != null;
1 ✔
973
        }
974

975
        /**
976
         * Used to re-read the state of the given instance from the underlying database.
977
         * @since 2.0
978
         * @param obj The object to refresh from the database in the session
979
         */
980
        public static void refreshEntity(Object obj) {
981
                log.trace("refreshing object: {}", obj);
1 ✔
982
                getContextDAO().refreshEntity(obj);
1 ✔
983
        }
1 ✔
984

985
        /**
986
         * Used to clear a cached object out of a session in the middle of a unit of work. Future
987
         * updates to this object will not be saved. Future gets of this object will not fetch this
988
         * cached copy
989
         *
990
         * @param obj The object to evict/remove from the session
991
         */
992
        public static void evictFromSession(Object obj) {
993
                log.trace("clearing session");
1 ✔
994
                getContextDAO().evictFromSession(obj);
1 ✔
995
        }
1 ✔
996

997
        /**
998
         * Evicts the entity data for a particular entity instance.
999
         *
1000
         * @param object entity instance to evict from the DB cache
1001
         */
1002
        public static void evictEntity(OpenmrsObject object) {
1003
                log.debug("Clearing DB cache for entity: {} with id: {}", object.getClass(), object.getId());
1 ✔
1004
                getContextDAO().evictEntity(object);
1 ✔
1005
        }
1 ✔
1006
        
1007
        /**
1008
         * Evicts all entity data of a particular class from the given region.
1009
         * 
1010
         * @param entityClass entity class to evict from the DB cache
1011
         */
1012
        public static void evictAllEntities(Class<?> entityClass) {
1013
                log.debug("Clearing DB cache for entities of type: {}", entityClass);
1 ✔
1014
                getContextDAO().evictAllEntities(entityClass);
1 ✔
1015
        }
1 ✔
1016
        
1017
        /**
1018
         * Evicts data from both DB and API cache.
1019
         */
1020
        public static void clearEntireCache() {
1021
                log.debug("Clearing DB cache");
1 ✔
1022
                getContextDAO().clearEntireCache();
1 ✔
1023
                log.debug("Clearing API cache");
1 ✔
1024
                getServiceContext().clearEntireApiCache();
1 ✔
1025
        }
1 ✔
1026
        
1027
        /**
1028
         * Starts the OpenMRS System Should be called prior to any kind of activity
1029
         *
1030
         * @param props Runtime properties to use for startup
1031
         * @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
1032
         *             cannot continue without input from the user
1033
         * @throws DatabaseUpdateException if database updates are required, see
1034
         *             {@link DatabaseUpdater#executeChangelog()}
1035
         * @throws ModuleMustStartException if a module that should be started is not able to
1036
         * @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
1037
         *      the required question/datatypes
1038
         */
1039
        public static synchronized void startup(Properties props) throws DatabaseUpdateException, InputRequiredException,
1040
        ModuleMustStartException {
1041
                // do any context database specific startup
1042
                getContextDAO().startup(props);
×
1043

1044
                if (getAdministrationService().isCoreSetupOnVersionChangeNeeded()) {
×
1045
                        log.info("Detected core version change. Running core setup hooks and Liquibase.");
×
1046
                        getAdministrationService().runCoreSetupOnVersionChange();
×
1047
                }
1048

1049
                // this should be first in the startup routines so that the application
1050
                // data directory can be set from the runtime properties
1051
                OpenmrsUtil.startup(props);
×
1052
                
1053
                openSession();
×
1054
                clearSession();
×
1055

1056
                // add any privileges/roles that /must/ exist for openmrs to work
1057
                // correctly.
1058
                checkCoreDataset();
×
1059
                
1060
                getContextDAO().setupSearchIndex();
×
1061

1062
                // Loop over each module and startup each with these custom properties
1063
                ModuleUtil.startup(props);
×
1064
        }
×
1065

1066
        /**
1067
         * Starts the OpenMRS System in a _non-webapp_ environment<br>
1068
         * <br>
1069
         * <b>Note:</b> This method calls {@link Context#openSession()}, so you must call
1070
         * {@link Context#closeSession()} somewhere on the same thread of this application so as to not
1071
         * leak memory.
1072
         *
1073
         * @param url database url like "jdbc:mysql://localhost:3306/openmrs?autoReconnect=true"
1074
         * @param username Connection username
1075
         * @param password Connection password
1076
         * @param properties Other startup properties
1077
         * @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
1078
         *             cannot continue without input from the user
1079
         * @throws DatabaseUpdateException if the database must be updated. See {@link DatabaseUpdater}
1080
         * @throws ModuleMustStartException if a module that should start is not able to
1081
         * @see #startup(Properties)
1082
         * @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
1083
         *      the required question/datatypes
1084
         */
1085
        public static synchronized void startup(String url, String username, String password, Properties properties)
1086
                        throws DatabaseUpdateException, InputRequiredException, ModuleMustStartException {
1087
                if (properties == null) {
×
1088
                        properties = new Properties();
×
1089
                }
1090

1091
                properties.put("connection.url", url);
×
1092
                properties.put("connection.username", username);
×
1093
                properties.put("connection.password", password);
×
1094
                setRuntimeProperties(properties);
×
1095

1096
                openSession(); // so that the startup method can use proxyPrivileges
×
1097

1098
                startup(properties);
×
1099
                
1100
                Context.getSchedulerService().onStartup();
×
1101
                
1102
                closeSession();
×
1103
        }
×
1104

1105
        /**
1106
         * Stops the OpenMRS System Should be called after all activity has ended and application is
1107
         * closing
1108
         */
1109
        public static void shutdown() {
1110
                log.debug("Shutting down the scheduler");
1 ✔
1111
                try {
1112
                        SchedulerService schedulerService = null;
1 ✔
1113
                        try {
1114
                                schedulerService = getSchedulerService();
1 ✔
1115
                        }
NEW
1116
                        catch (APIException e) {
×
1117
                                // pass
1118
                        }
1 ✔
1119
                        if (schedulerService != null) {
1 ✔
1120
                                // stops running tasks before the modules and the database they use are shut down
1121
                                schedulerService.onShutdown();
1 ✔
1122
                        }
1123
                }
NEW
1124
                catch (Exception e) {
×
NEW
1125
                        log.warn("Error while shutting down the scheduler", e);
×
1126
                }
1 ✔
1127

1128
                log.debug("Shutting down the modules");
1 ✔
1129
                try {
1130
                        ModuleUtil.shutdown();
1 ✔
1131
                }
1132
                catch (Exception e) {
×
1133
                        log.warn("Error while shutting down module system", e);
×
1134
                }
1 ✔
1135

1136
                log.debug("Shutting down the context");
1 ✔
1137
                try {
1138
                        ContextDAO dao = null;
1 ✔
1139
                        try {
1140
                                dao = getContextDAO();
1 ✔
1141
                        }
1142
                        catch (APIException e) {
×
1143
                                // pass
1144
                        }
1 ✔
1145
                        if (dao != null) {
1 ✔
1146
                                dao.shutdown();
1 ✔
1147
                        }
1148
                }
1149
                catch (Exception e) {
×
1150
                        log.warn("Error while shutting down context dao", e);
×
1151
                }
1 ✔
1152
        }
1 ✔
1153

1154
        /**
1155
         * Used for getting services not in the previous get*Service() calls
1156
         *
1157
         * @param cls The Class of the service to get
1158
         * @return The requested Service
1159
         * <strong>Should</strong> return the same object when called multiple times for the same class
1160
         */
1161
        public static <T> T getService(Class<? extends T> cls) {
1162
                return getServiceContext().getService(cls);
1 ✔
1163
        }
1164

1165
        /**
1166
         * Adds an AOP advisor around the given Class <code>cls</code>
1167
         * <p>
1168
         * Advisors can wrap around a method and effect the method before or after
1169
         *
1170
         * @param cls
1171
         * @param advisor
1172
         */
1173
        public static void addAdvisor(Class cls, Advisor advisor) {
1174
                getServiceContext().addAdvisor(cls, advisor);
×
1175
        }
×
1176

1177
        /**
1178
         * Adds an AOP advice object around the given Class <code>cls</code>
1179
         * <p>
1180
         * Advice comes in the form of before or afterReturning methods
1181
         *
1182
         * @param cls
1183
         * @param advice
1184
         */
1185
        public static void addAdvice(Class cls, Advice advice) {
1186
                getServiceContext().addAdvice(cls, advice);
×
1187
        }
×
1188

1189
        /**
1190
         * Removes the given AOP advisor from Class <code>cls</code>
1191
         *
1192
         * @param cls
1193
         * @param advisor
1194
         */
1195
        public static void removeAdvisor(Class cls, Advisor advisor) {
1196
                getServiceContext().removeAdvisor(cls, advisor);
×
1197
        }
×
1198

1199
        /**
1200
         * Removes the given AOP advice object from Class <code>cls</code>
1201
         *
1202
         * @param cls
1203
         * @param advice
1204
         */
1205
        public static void removeAdvice(Class cls, Advice advice) {
1206
                getServiceContext().removeAdvice(cls, advice);
×
1207
        }
×
1208

1209
        /**
1210
         * Runs through the core data (e.g. privileges, roles, and global properties) and adds them if
1211
         * necessary.
1212
         */
1213
        public static void checkCoreDataset() {
1214
                // setting core roles
1215
                try {
1216
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_ROLES);
×
1217
                        Set<String> currentRoleNames = new HashSet<>();
×
1218
                        for (Role role : Context.getUserService().getAllRoles()) {
×
1219
                                currentRoleNames.add(role.getRole().toUpperCase());
×
1220
                        }
×
1221
                        Map<String, String> map = OpenmrsUtil.getCoreRoles();
×
1222
                        for (Map.Entry<String, String> entry : map.entrySet()) {
×
1223
                                String roleName = entry.getKey();
×
1224
                                if (!currentRoleNames.contains(roleName.toUpperCase())) {
×
1225
                                        Role role = new Role();
×
1226
                                        role.setRole(roleName);
×
1227
                                        role.setDescription(entry.getValue());
×
1228
                                        Context.getUserService().saveRole(role);
×
1229
                                }
1230
                        }
×
1231
                }
1232
                catch (Exception e) {
×
1233
                        log.error("Error while setting core roles for openmrs system", e);
×
1234
                }
1235
                finally {
1236
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_ROLES);
×
1237
                }
1238

1239
                // setting core privileges
1240
                try {
1241
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_PRIVILEGES);
×
1242
                        Set<String> currentPrivilegeNames = new HashSet<>();
×
1243
                        for (Privilege privilege : Context.getUserService().getAllPrivileges()) {
×
1244
                                currentPrivilegeNames.add(privilege.getPrivilege().toUpperCase());
×
1245
                        }
×
1246
                        Map<String, String> map = OpenmrsUtil.getCorePrivileges();
×
1247
                        for (Map.Entry<String, String> entry : map.entrySet()) {
×
1248
                                String privilegeName = entry.getKey();
×
1249
                                if (!currentPrivilegeNames.contains(privilegeName.toUpperCase())) {
×
1250
                                        Privilege p = new Privilege();
×
1251
                                        p.setPrivilege(privilegeName);
×
1252
                                        p.setDescription(entry.getValue());
×
1253
                                        Context.getUserService().savePrivilege(p);
×
1254
                                }
1255
                        }
×
1256
                }
1257
                catch (Exception e) {
×
1258
                        log.error("Error while setting core privileges", e);
×
1259
                }
1260
                finally {
1261
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_PRIVILEGES);
×
1262
                }
1263

1264
                // setting core global properties
1265
                try {
1266
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES);
×
1267
                        Context.addProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
1268
                        Set<String> currentPropNames = new HashSet<>();
×
1269
                        Map<String, GlobalProperty> propsMissingDescription = new HashMap<>();
×
1270
                        Map<String, GlobalProperty> propsMissingDatatype = new HashMap<>();
×
1271
                        for (GlobalProperty prop : Context.getAdministrationService().getAllGlobalProperties()) {
×
1272
                                currentPropNames.add(prop.getProperty().toUpperCase());
×
1273
                                if (prop.getDescription() == null) {
×
1274
                                        propsMissingDescription.put(prop.getProperty().toUpperCase(), prop);
×
1275
                                }
1276
                                if (prop.getDatatypeClassname() == null) {
×
1277
                                        propsMissingDatatype.put(prop.getProperty().toUpperCase(), prop);
×
1278
                                }
1279
                        }
×
1280

1281
                        for (GlobalProperty coreProp : OpenmrsConstants.CORE_GLOBAL_PROPERTIES()) {
×
1282
                                String corePropName = coreProp.getProperty().toUpperCase();
×
1283
                                // if the prop doesn't exist, save it
1284
                                if (!currentPropNames.contains(corePropName)) {
×
1285
                                        Context.getAdministrationService().saveGlobalProperty(coreProp);
×
1286
                                        currentPropNames.add(corePropName); // add to list in case
×
1287
                                        // of duplicates
1288
                                } else {
1289
                                        // if the prop is missing its description, update it
1290
                                        GlobalProperty propToUpdate = propsMissingDescription.get(corePropName);
×
1291
                                        if (propToUpdate != null) {
×
1292
                                                propToUpdate.setDescription(coreProp.getDescription());
×
1293
                                                Context.getAdministrationService().saveGlobalProperty(propToUpdate);
×
1294
                                        }
1295
                                        // set missing datatypes
1296
                                        propToUpdate = propsMissingDatatype.get(corePropName);
×
1297
                                        if (propToUpdate != null && coreProp.getDatatypeClassname() != null) {
×
1298
                                                propToUpdate.setDatatypeClassname(coreProp.getDatatypeClassname());
×
1299
                                                propToUpdate.setDatatypeConfig(coreProp.getDatatypeConfig());
×
1300
                                                propToUpdate.setPreferredHandlerClassname(coreProp.getPreferredHandlerClassname());
×
1301
                                                propToUpdate.setHandlerConfig(coreProp.getHandlerConfig());
×
1302
                                                Context.getAdministrationService().saveGlobalProperty(propToUpdate);
×
1303
                                        }
1304
                                }
1305
                        }
×
1306
                }
1307
                catch (Exception e) {
×
1308
                        log.error("Error while setting core global properties", e);
×
1309
                }
1310
                finally {
1311
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES);
×
1312
                        Context.removeProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
1313
                }
1314

1315
                // setting default validation rule
1316
                AdministrationService as = Context.getAdministrationService();
×
1317
                Boolean disableValidation = Boolean.valueOf(as.getGlobalProperty(OpenmrsConstants.GP_DISABLE_VALIDATION, "false"));
×
1318
                ValidateUtil.setDisableValidation(disableValidation);
×
1319

1320
                PersonName.setFormat(Context.getAdministrationService().getGlobalProperty(
×
1321
                                OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_FORMAT));
1322

1323
                Allergen.setOtherNonCodedConceptUuid(Context.getAdministrationService().getGlobalProperty(
×
1324
                                OpenmrsConstants.GP_ALLERGEN_OTHER_NON_CODED_UUID));
1325
        }
×
1326

1327
        /**
1328
         * Updates the openmrs database to the latest. This is only needed if using the API alone. <br>
1329
         * <br>
1330
         * The typical use-case would be: Try to {@link #startup(String, String, String, Properties)},
1331
         * if that fails, call this method to get the database up to speed.
1332
         *
1333
         * @param userInput (can be null) responses from the user about needed input
1334
         * @throws DatabaseUpdateException if an error occurred while updating
1335
         * @since 1.5
1336
         * @deprecated as of 2.4
1337
         * 
1338
         */
1339
        @Deprecated
1340
        public static void updateDatabase(Map<String, Object> userInput) throws DatabaseUpdateException {
1341
                throw new UnsupportedOperationException("As of 2.4, this method is not longer implemented");
×
1342
        }
1343

1344
        /**
1345
         * Gets the simple date format for the current user's locale. The format will be similar in size
1346
         * to mm/dd/yyyy
1347
         *
1348
         * @return SimpleDateFormat for the user's current locale
1349
         * @see org.openmrs.util.OpenmrsUtil#getDateFormat(Locale)
1350
         * <strong>Should</strong> return a pattern with four y characters in it
1351
         */
1352
        public static SimpleDateFormat getDateFormat() {
1353
                return OpenmrsUtil.getDateFormat(getLocale());
1 ✔
1354
        }
1355

1356
        /**
1357
         * Gets the simple time format for the current user's locale. The format will be similar to
1358
         * hh:mm a
1359
         *
1360
         * @return SimpleDateFormat for the user's current locale
1361
         * @see org.openmrs.util.OpenmrsUtil#getTimeFormat(Locale)
1362
         * <strong>Should</strong> return a pattern with two h characters in it
1363
         */
1364
        public static SimpleDateFormat getTimeFormat() {
1365
                return OpenmrsUtil.getTimeFormat(getLocale());
×
1366
        }
1367

1368
        /**
1369
         * Gets the simple datetime format for the current user's locale. The format will be similar to
1370
         * mm/dd/yyyy hh:mm a
1371
         *
1372
         * @return SimpleDateFormat for the user's current locale
1373
         * @see org.openmrs.util.OpenmrsUtil#getDateTimeFormat(Locale)
1374
         * <strong>Should</strong> return a pattern with four y characters and two h characters in it
1375
         */
1376
        public static SimpleDateFormat getDateTimeFormat() {
1377
                return OpenmrsUtil.getDateTimeFormat(getLocale());
1 ✔
1378
        }
1379

1380
        /**
1381
         * @return true/false whether the service context is currently being refreshed
1382
         * @see org.openmrs.api.context.ServiceContext#isRefreshingContext()
1383
         */
1384
        public static boolean isRefreshingContext() {
1385
                return getServiceContext().isRefreshingContext();
1 ✔
1386
        }
1387

1388
        /**
1389
         * @since 1.5
1390
         * @see ServiceContext#getRegisteredComponents(Class)
1391
         */
1392
        public static <T> List<T> getRegisteredComponents(Class<T> type) {
1393
                return getServiceContext().getRegisteredComponents(type);
1 ✔
1394
        }
1395

1396
        /**
1397
         * @see ServiceContext#getRegisteredComponent(String, Class)
1398
         * @since 1.9.4
1399
         */
1400
        public static <T> T getRegisteredComponent(String beanName, Class<T> type) throws APIException {
1401
                return getServiceContext().getRegisteredComponent(beanName, type);
1 ✔
1402
        }
1403

1404
        /**
1405
         * @see ServiceContext#getModuleOpenmrsServices(String)
1406
         * @since 1.9
1407
         */
1408
        public static List<OpenmrsService> getModuleOpenmrsServices(String modulePackage) {
1409
                return getServiceContext().getModuleOpenmrsServices(modulePackage);
1 ✔
1410
        }
1411

1412
        /**
1413
         * @since 1.9
1414
         * @see ServiceContext#getVisitService()
1415
         */
1416
        public static VisitService getVisitService() {
1417
                return getServiceContext().getVisitService();
1 ✔
1418
        }
1419

1420
        /**
1421
         * @since 1.9
1422
         * @see ServiceContext#getProviderService()
1423
         */
1424
        public static ProviderService getProviderService() {
1425
                return getServiceContext().getProviderService();
1 ✔
1426
        }
1427

1428
        /**
1429
         * @since 1.9
1430
         * @see ServiceContext#getDatatypeService()
1431
         */
1432
        public static DatatypeService getDatatypeService() {
1433
                return getServiceContext().getDatatypeService();
1 ✔
1434
        }
1435

1436
        /**
1437
         * Add or replace a property in the config properties list
1438
         *
1439
         * @param key name of the property
1440
         * @param value value of the property
1441
         * @since 1.9
1442
         */
1443
        public static void addConfigProperty(Object key, Object value) {
1444
                configProperties.put(key, value);
×
1445
        }
×
1446

1447
        /**
1448
         * Remove a property from the list of config properties
1449
         *
1450
         * @param key name of the property
1451
         * @since 1.9
1452
         */
1453
        public static void removeConfigProperty(Object key) {
1454
                configProperties.remove(key);
×
1455
        }
×
1456

1457
        /**
1458
         * Get the config properties that have been added to this OpenMRS instance
1459
         *
1460
         * @return copy of the module properties
1461
         * @since 1.9
1462
         */
1463
        public static Properties getConfigProperties() {
1464
                Properties props = new Properties();
1 ✔
1465
                props.putAll(configProperties);
1 ✔
1466
                return props;
1 ✔
1467
        }
1468

1469
        /**
1470
         * Updates the search index. It is a blocking operation, which may take even a few minutes
1471
         * depending on the index size.
1472
         * <p>
1473
         * There is no need to call this method in normal usage since the index is automatically updated
1474
         * whenever DB transactions are committed.
1475
         * <p>
1476
         * The method is designated to be used in tests, which rollback transactions. Note that if the
1477
         * transaction is rolled back, changes to the index will not be reverted.
1478
         *
1479
         * @since 1.11
1480
         */
1481
        public static void updateSearchIndex() {
1482
                getContextDAO().updateSearchIndex();
×
1483
        }
×
1484

1485
        /**
1486
         * Updates the search index. It is an asynchronous operation.
1487
         * <p>
1488
         * There is no need to call this method in normal usage since the index is automatically updated
1489
         * whenever DB transactions are committed.
1490
         * <p>
1491
         *
1492
         * @return object representing the result of the started asynchronous operation
1493
         */
1494
        public static Future<?> updateSearchIndexAsync() {
1495
                return getContextDAO().updateSearchIndexAsync();
×
1496
        }
1497

1498
        /**
1499
         * It should be used <b>IN TESTS ONLY</b>. See {@link #updateSearchIndex(Class[])} for normal use.
1500
         * <p>
1501
         * Updates the search index for objects of the given type.
1502
         *
1503
         * @see #updateSearchIndex()
1504
         * @see #updateSearchIndex(Class[]) 
1505
         * @param type
1506
         * @since 1.11
1507
         */
1508
        public static void updateSearchIndexForType(Class<?> type) {
1509
                getContextDAO().updateSearchIndexForType(type);
1 ✔
1510
        }
1 ✔
1511

1512
        /**
1513
         * Updates the search index for objects of the given types using mass indexer.
1514
         * 
1515
         * @see #updateSearchIndex() 
1516
         * @param types
1517
         * @since 2.8.0
1518
         */
1519
        public static void updateSearchIndex(Class<?>... types) {
1520
                getContextDAO().updateSearchIndex(types);
×
1521
        }
×
1522

1523
        /**
1524
         * Updates the search index for the given object.
1525
         *
1526
         * @see #updateSearchIndex()
1527
         * @param object
1528
         * @since 1.11
1529
         */
1530
        public static void updateSearchIndexForObject(Object object) {
1531
                getContextDAO().updateSearchIndexForObject(object);
×
1532
        }
×
1533

1534
        /**
1535
         * @see org.openmrs.api.context.ServiceContext#setUseSystemClassLoader(boolean)
1536
         * @since 1.10
1537
         */
1538
        public static void setUseSystemClassLoader(boolean useSystemClassLoader) {
1539
                getServiceContext().setUseSystemClassLoader(useSystemClassLoader);
1 ✔
1540
        }
1 ✔
1541

1542
        /**
1543
         * @see org.openmrs.api.context.ServiceContext#isUseSystemClassLoader()
1544
         * @since 1.10
1545
         */
1546
        public static boolean isUseSystemClassLoader() {
1547
                return getServiceContext().isUseSystemClassLoader();
×
1548
        }
1549

1550
        /**
1551
         * @return a Connection from the OpenMRS database connection pool
1552
         * @since 2.5.7
1553
         */
1554
        public static Connection getDatabaseConnection() {
1555
                return getContextDAO().getDatabaseConnection();
×
1556
        }
1557

1558
        /**
1559
         * It is used to shorten startup time by e.g. not running Liquibase checks, if versions did not change or 
1560
         * re-using expanded jars between restarts. If you want to force a standard startup
1561
         * procedure without optimization, please set the <code>optimized.startup</code> runtime property to <code>false</code>.
1562
         * (<code>true</code> by default) 
1563
         * <p>
1564
         * See <a href="https://issues.openmrs.org/browse/TRUNK-6417">TRUNK-6417</a>
1565
         * 
1566
         * @return <code>true</code> (default) or <code>false</code>
1567
         * @since 2.9.0
1568
         */
1569
        public static boolean isOptimizedStartup() {
1570
                return Boolean.parseBoolean(Context.getRuntimeProperties().getProperty("optimized.startup", "true"));
1 ✔
1571
        }
1572
}
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