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

openmrs / openmrs-core / 30399987753

28 Jul 2026 09:16PM UTC coverage: 66.493% (+0.2%) from 66.313%
30399987753

push

github

ibacher
Fix Mockito inline mocks on JDK 8 and 11 (#6403)

24796 of 37291 relevant lines covered (66.49%)

0.67 hits per line

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

48.67
/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 org.aopalliance.aop.Advice;
13
import org.apache.commons.lang3.StringUtils;
14
import org.openmrs.Allergen;
15
import org.openmrs.GlobalProperty;
16
import org.openmrs.OpenmrsObject;
17
import org.openmrs.PersonName;
18
import org.openmrs.Privilege;
19
import org.openmrs.Role;
20
import org.openmrs.User;
21
import org.openmrs.api.APIException;
22
import org.openmrs.api.AdministrationService;
23
import org.openmrs.api.CohortService;
24
import org.openmrs.api.ConceptService;
25
import org.openmrs.api.ConditionService;
26
import org.openmrs.api.DatatypeService;
27
import org.openmrs.api.DiagnosisService;
28
import org.openmrs.api.EncounterService;
29
import org.openmrs.api.FormService;
30
import org.openmrs.api.LocationService;
31
import org.openmrs.api.MedicationDispenseService;
32
import org.openmrs.api.ObsService;
33
import org.openmrs.api.OpenmrsService;
34
import org.openmrs.api.OrderService;
35
import org.openmrs.api.OrderSetService;
36
import org.openmrs.api.PatientService;
37
import org.openmrs.api.PersonService;
38
import org.openmrs.api.ProgramWorkflowService;
39
import org.openmrs.api.ProviderService;
40
import org.openmrs.api.SerializationService;
41
import org.openmrs.api.UserService;
42
import org.openmrs.api.VisitService;
43
import org.openmrs.api.db.ContextDAO;
44
import org.openmrs.hl7.HL7Service;
45
import org.openmrs.logic.LogicService;
46
import org.openmrs.messagesource.MessageSourceService;
47
import org.openmrs.module.ModuleMustStartException;
48
import org.openmrs.module.ModuleUtil;
49
import org.openmrs.notification.AlertService;
50
import org.openmrs.notification.MessageException;
51
import org.openmrs.notification.MessagePreparator;
52
import org.openmrs.notification.MessageSender;
53
import org.openmrs.notification.MessageService;
54
import org.openmrs.notification.mail.MailMessageSender;
55
import org.openmrs.notification.mail.velocity.VelocityMessagePreparator;
56
import org.openmrs.scheduler.SchedulerService;
57
import org.openmrs.scheduler.SchedulerUtil;
58
import org.openmrs.util.ConfigUtil;
59
import org.openmrs.util.DatabaseUpdateException;
60
import org.openmrs.util.DatabaseUpdater;
61
import org.openmrs.util.InputRequiredException;
62
import org.openmrs.util.LocaleUtility;
63
import org.openmrs.util.OpenmrsClassLoader;
64
import org.openmrs.util.OpenmrsConstants;
65
import org.openmrs.util.OpenmrsUtil;
66
import org.openmrs.util.PrivilegeConstants;
67
import org.openmrs.validator.ValidateUtil;
68
import org.slf4j.Logger;
69
import org.slf4j.LoggerFactory;
70
import org.springframework.aop.Advisor;
71
import org.springframework.beans.BeansException;
72
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
73
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
74

75
import javax.mail.Authenticator;
76
import javax.mail.PasswordAuthentication;
77
import javax.mail.Session;
78
import java.sql.Connection;
79
import java.text.SimpleDateFormat;
80
import java.util.Arrays;
81
import java.util.HashMap;
82
import java.util.HashSet;
83
import java.util.List;
84
import java.util.Locale;
85
import java.util.Map;
86
import java.util.Properties;
87
import java.util.Set;
88
import java.util.concurrent.Future;
89

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

138

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

142
        private static volatile Session mailSession;
143

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

148
        private static volatile ServiceContext serviceContext;
149

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

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

154
        private static AuthenticationScheme authenticationScheme;
155

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

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

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

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

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

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

194
                authenticationScheme = new UsernamePasswordAuthenticationScheme();
1✔
195

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

697
        /**
698
         * logs out the "active" (authenticated) user within context
699
         *
700
         * @see #authenticate
701
         * <strong>Should</strong> not fail if session hasn't been opened yet
702
         */
703
        public static void logout() {
704
                if (!isSessionOpen()) {
1✔
705
                        return; // fail early if there isn't even a session open
1✔
706
                }
707
                log.debug("Logging out : {}", getAuthenticatedUser());
1✔
708

709
                getUserContext().logout();
1✔
710

711
                // reset the UserContext object (usually cleared out by closeSession()
712
                // soon after this)
713
                setUserContext(new UserContext(getAuthenticationScheme()));
1✔
714
        }
1✔
715

716
        /**
717
         * Convenience method. Passes through to userContext.getAllRoles(User)
718
         */
719
        public static Set<Role> getAllRoles(User user) throws Exception {
720
                return getUserContext().getAllRoles();
×
721
        }
722

723
        /**
724
         * Tests whether the currently authenticated user has a particular privilege
725
         */
726
        public static boolean hasPrivilege(String privilege) {
727
                // the daemon threads have access to all things
728
                if (Daemon.isDaemonThread()) {
1✔
729
                        return true;
1✔
730
                }
731

732
                return getUserContext().hasPrivilege(privilege);
1✔
733
        }
734

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

753
                return getUserContext().hasPrivilege(privilege, includeProxyPrivileges);
1✔
754
        }
755

756
        /**
757
         * Throws an exception if the currently authenticated user does not have the specified
758
         * privilege.
759
         *
760
         * @param privilege
761
         * @throws ContextAuthenticationException
762
         */
763
        public static void requirePrivilege(String privilege) throws ContextAuthenticationException {
764
                if (!hasPrivilege(privilege)) {
1✔
765
                        String errorMessage;
766
                        if (StringUtils.isNotBlank(privilege)) {
×
767
                                errorMessage = Context.getMessageSourceService().getMessage("error.privilegesRequired",
×
768
                                                new Object[] { privilege }, null);
769
                        } else {
770
                                //Should we even be here if the privilege is blank?
771
                                errorMessage = Context.getMessageSourceService().getMessage("error.privilegesRequiredNoArgs");
×
772
                        }
773

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

837
        /**
838
         * Removes one or more privileges from the list of privileges that that {@link #hasPrivilege(String)} will
839
         * regard as available regardless of whether the user would otherwise have the privilege.
840
         * <p/>
841
         * This is the compliment for {@link #addProxyPrivilege(String...)} to clean-up the context.
842
         * <p/>
843
         *
844
         * @param privilege privileges to remove in string form
845
         * @see #hasPrivilege(String)
846
         * @see #addProxyPrivilege(String)
847
         */
848
        public static void removeProxyPrivilege(String privilege) {
849
                getUserContext().removeProxyPrivilege(privilege);
1✔
850
        }
1✔
851

852
        /**
853
         * Removes a privilege from the list of privileges that that {@link #hasPrivilege(String)} will
854
         * regard as available regardless of whether the user would otherwise have the privilege.
855
         * <p/>
856
         * This is the compliment for {@link #addProxyPrivilege(String...)} to clean-up the context.
857
         * <p/>
858
         *
859
         * @param privileges privileges to remove in string form
860
         * @see #hasPrivilege(String)
861
         * @see #addProxyPrivilege(String...)
862
         * * @since 3.0.0, 2.8.2, 2.7.8
863
         */
864
        public static void removeProxyPrivilege(String... privileges) {
865
                getUserContext().removeProxyPrivilege(privileges);
1✔
866
        }
1✔
867

868
        /**
869
         * Convenience method. Passes through to {@link UserContext#setLocale(Locale)}
870
         */
871
        public static void setLocale(Locale locale) {
872
                getUserContext().setLocale(locale);
1✔
873
        }
1✔
874

875
        /**
876
         * Convenience method. Passes through to {@link UserContext#getLocale()}
877
         *
878
         * <strong>Should</strong> not fail if session hasn't been opened
879
         */
880
        public static Locale getLocale() {
881
                // if a session hasn't been opened, just fetch the default
882
                if (!isSessionOpen()) {
1✔
883
                        return LocaleUtility.getDefaultLocale();
1✔
884
                }
885

886
                return getUserContext().getLocale();
1✔
887
        }
888

889
        /**
890
         * Used to define a unit of work. All "units of work" should be surrounded by openSession and
891
         * closeSession calls.
892
         */
893
        public static void openSession() {
894
                log.trace("opening session");
1✔
895
                setUserContext(new UserContext(getAuthenticationScheme())); // must be cleared out in
1✔
896
                // closeSession()
897
                getContextDAO().openSession();
1✔
898
        }
1✔
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 closeSession() {
905
                log.trace("closing session");
1✔
906
                clearUserContext(); // because we set a UserContext on the current
1✔
907
                // thread in openSession()
908
                getContextDAO().closeSession();
1✔
909
        }
1✔
910

911
        /**
912
         * Used to define a unit of work which does not require clearing out the currently authenticated
913
         * user. Remember to call closeSessionWithCurrentUser in a, preferably, finally block after this
914
         * work.
915
         *
916
         * @since 1.10
917
         */
918
        public static void openSessionWithCurrentUser() {
919
                getContextDAO().openSession();
×
920
        }
×
921

922
        /**
923
         * Used when the a unit of work which started with a call for openSessionWithCurrentUser has
924
         * finished. This should be in a, preferably, finally block.
925
         *
926
         * @since 1.10
927
         */
928
        public static void closeSessionWithCurrentUser() {
929
                getContextDAO().closeSession();
×
930
        }
×
931

932
        /**
933
         * Clears cached changes made so far during this unit of work without writing them to the
934
         * database. If you call this method, and later call closeSession() or flushSession() your
935
         * changes are still lost.
936
         */
937
        public static void clearSession() {
938
                log.trace("clearing session");
1✔
939
                getContextDAO().clearSession();
1✔
940
        }
1✔
941

942
        /**
943
         * Forces any changes made so far in this unit of work to be written to the database
944
         *
945
         * @since 1.6
946
         */
947
        public static void flushSession() {
948
                log.trace("flushing session");
1✔
949
                getContextDAO().flushSession();
1✔
950
        }
1✔
951

952
        /**
953
         * This method tells whether {@link #openSession()} has been called or not already. If it hasn't
954
         * been called, some methods won't work correctly because a {@link UserContext} isn't available.
955
         *
956
         * @return true if {@link #openSession()} has been called already.
957
         * @since 1.5
958
         * <strong>Should</strong> return true if session is closed
959
         */
960
        public static boolean isSessionOpen() {
961
                return userContextHolder.get() != null;
1✔
962
        }
963

964
        /**
965
         * Used to re-read the state of the given instance from the underlying database.
966
         * @since 2.0
967
         * @param obj The object to refresh from the database in the session
968
         */
969
        public static void refreshEntity(Object obj) {
970
                log.trace("refreshing object: {}", obj);
1✔
971
                getContextDAO().refreshEntity(obj);
1✔
972
        }
1✔
973

974
        /**
975
         * Used to clear a cached object out of a session in the middle of a unit of work. Future
976
         * updates to this object will not be saved. Future gets of this object will not fetch this
977
         * cached copy
978
         *
979
         * @param obj The object to evict/remove from the session
980
         */
981
        public static void evictFromSession(Object obj) {
982
                log.trace("clearing session");
1✔
983
                getContextDAO().evictFromSession(obj);
1✔
984
        }
1✔
985

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

1033
                // find/set/check whether the current database version is compatible
1034
                checkForDatabaseUpdates(props);
×
1035

1036
                // this should be first in the startup routines so that the application
1037
                // data directory can be set from the runtime properties
1038
                OpenmrsUtil.startup(props);
×
1039

1040
                openSession();
×
1041
                clearSession();
×
1042

1043
                // add any privileges/roles that /must/ exist for openmrs to work
1044
                // correctly.
1045
                checkCoreDataset();
×
1046

1047
                getContextDAO().setupSearchIndex();
×
1048

1049
                // Loop over each module and startup each with these custom properties
1050
                ModuleUtil.startup(props);
×
1051
        }
×
1052

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

1078
                properties.put("connection.url", url);
×
1079
                properties.put("connection.username", username);
×
1080
                properties.put("connection.password", password);
×
1081
                setRuntimeProperties(properties);
×
1082

1083
                openSession(); // so that the startup method can use proxyPrivileges
×
1084

1085
                startup(properties);
×
1086

1087
                // start the scheduled tasks
1088
                SchedulerUtil.startup(properties);
×
1089

1090
                closeSession();
×
1091
        }
×
1092

1093
        /**
1094
         * Stops the OpenMRS System Should be called after all activity has ended and application is
1095
         * closing
1096
         */
1097
        public static void shutdown() {
1098
                log.debug("Shutting down the scheduler");
×
1099
                try {
1100
                        // Needs to be shutdown before Hibernate
1101
                        SchedulerUtil.shutdown();
×
1102
                }
1103
                catch (Exception e) {
×
1104
                        log.warn("Error while shutting down scheduler service", e);
×
1105
                }
×
1106

1107
                log.debug("Shutting down the modules");
×
1108
                try {
1109
                        ModuleUtil.shutdown();
×
1110
                }
1111
                catch (Exception e) {
×
1112
                        log.warn("Error while shutting down module system", e);
×
1113
                }
×
1114

1115
                log.debug("Shutting down the context");
×
1116
                try {
1117
                        ContextDAO dao = null;
×
1118
                        try {
1119
                                dao = getContextDAO();
×
1120
                        }
1121
                        catch (APIException e) {
×
1122
                                // pass
1123
                        }
×
1124
                        if (dao != null) {
×
1125
                                dao.shutdown();
×
1126
                        }
1127
                }
1128
                catch (Exception e) {
×
1129
                        log.warn("Error while shutting down context dao", e);
×
1130
                }
×
1131
        }
×
1132

1133
        /**
1134
         * Used for getting services not in the previous get*Service() calls
1135
         *
1136
         * @param cls The Class of the service to get
1137
         * @return The requested Service
1138
         * <strong>Should</strong> return the same object when called multiple times for the same class
1139
         */
1140
        public static <T> T getService(Class<? extends T> cls) {
1141
                return getServiceContext().getService(cls);
1✔
1142
        }
1143

1144
        /**
1145
         * Adds an AOP advisor around the given Class <code>cls</code>
1146
         * <p>
1147
         * Advisors can wrap around a method and effect the method before or after
1148
         *
1149
         * @param cls
1150
         * @param advisor
1151
         */
1152
        public static void addAdvisor(Class cls, Advisor advisor) {
1153
                getServiceContext().addAdvisor(cls, advisor);
×
1154
        }
×
1155

1156
        /**
1157
         * Adds an AOP advice object around the given Class <code>cls</code>
1158
         * <p>
1159
         * Advice comes in the form of before or afterReturning methods
1160
         *
1161
         * @param cls
1162
         * @param advice
1163
         */
1164
        public static void addAdvice(Class cls, Advice advice) {
1165
                getServiceContext().addAdvice(cls, advice);
×
1166
        }
×
1167

1168
        /**
1169
         * Removes the given AOP advisor from Class <code>cls</code>
1170
         *
1171
         * @param cls
1172
         * @param advisor
1173
         */
1174
        public static void removeAdvisor(Class cls, Advisor advisor) {
1175
                getServiceContext().removeAdvisor(cls, advisor);
×
1176
        }
×
1177

1178
        /**
1179
         * Removes the given AOP advice object from Class <code>cls</code>
1180
         *
1181
         * @param cls
1182
         * @param advice
1183
         */
1184
        public static void removeAdvice(Class cls, Advice advice) {
1185
                getServiceContext().removeAdvice(cls, advice);
×
1186
        }
×
1187

1188
        /**
1189
         * Runs through the core data (e.g. privileges, roles, and global properties) and adds them if
1190
         * necessary.
1191
         */
1192
        public static void checkCoreDataset() {
1193
                // setting core roles
1194
                try {
1195
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_ROLES);
×
1196
                        Set<String> currentRoleNames = new HashSet<>();
×
1197
                        for (Role role : Context.getUserService().getAllRoles()) {
×
1198
                                currentRoleNames.add(role.getRole().toUpperCase());
×
1199
                        }
×
1200
                        Map<String, String> map = OpenmrsUtil.getCoreRoles();
×
1201
                        for (Map.Entry<String, String> entry : map.entrySet()) {
×
1202
                                String roleName = entry.getKey();
×
1203
                                if (!currentRoleNames.contains(roleName.toUpperCase())) {
×
1204
                                        Role role = new Role();
×
1205
                                        role.setRole(roleName);
×
1206
                                        role.setDescription(entry.getValue());
×
1207
                                        Context.getUserService().saveRole(role);
×
1208
                                }
1209
                        }
×
1210
                }
1211
                catch (Exception e) {
×
1212
                        log.error("Error while setting core roles for openmrs system", e);
×
1213
                }
1214
                finally {
1215
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_ROLES);
×
1216
                }
1217

1218
                // setting core privileges
1219
                try {
1220
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_PRIVILEGES);
×
1221
                        Set<String> currentPrivilegeNames = new HashSet<>();
×
1222
                        for (Privilege privilege : Context.getUserService().getAllPrivileges()) {
×
1223
                                currentPrivilegeNames.add(privilege.getPrivilege().toUpperCase());
×
1224
                        }
×
1225
                        Map<String, String> map = OpenmrsUtil.getCorePrivileges();
×
1226
                        for (Map.Entry<String, String> entry : map.entrySet()) {
×
1227
                                String privilegeName = entry.getKey();
×
1228
                                if (!currentPrivilegeNames.contains(privilegeName.toUpperCase())) {
×
1229
                                        Privilege p = new Privilege();
×
1230
                                        p.setPrivilege(privilegeName);
×
1231
                                        p.setDescription(entry.getValue());
×
1232
                                        Context.getUserService().savePrivilege(p);
×
1233
                                }
1234
                        }
×
1235
                }
1236
                catch (Exception e) {
×
1237
                        log.error("Error while setting core privileges", e);
×
1238
                }
1239
                finally {
1240
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_PRIVILEGES);
×
1241
                }
1242

1243
                // setting core global properties
1244
                try {
1245
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES);
×
1246
                        Context.addProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
1247
                        Set<String> currentPropNames = new HashSet<>();
×
1248
                        Map<String, GlobalProperty> propsMissingDescription = new HashMap<>();
×
1249
                        Map<String, GlobalProperty> propsMissingDatatype = new HashMap<>();
×
1250
                        for (GlobalProperty prop : Context.getAdministrationService().getAllGlobalProperties()) {
×
1251
                                currentPropNames.add(prop.getProperty().toUpperCase());
×
1252
                                if (prop.getDescription() == null) {
×
1253
                                        propsMissingDescription.put(prop.getProperty().toUpperCase(), prop);
×
1254
                                }
1255
                                if (prop.getDatatypeClassname() == null) {
×
1256
                                        propsMissingDatatype.put(prop.getProperty().toUpperCase(), prop);
×
1257
                                }
1258
                        }
×
1259

1260
                        for (GlobalProperty coreProp : OpenmrsConstants.CORE_GLOBAL_PROPERTIES()) {
×
1261
                                String corePropName = coreProp.getProperty().toUpperCase();
×
1262
                                // if the prop doesn't exist, save it
1263
                                if (!currentPropNames.contains(corePropName)) {
×
1264
                                        Context.getAdministrationService().saveGlobalProperty(coreProp);
×
1265
                                        currentPropNames.add(corePropName); // add to list in case
×
1266
                                        // of duplicates
1267
                                } else {
1268
                                        // if the prop is missing its description, update it
1269
                                        GlobalProperty propToUpdate = propsMissingDescription.get(corePropName);
×
1270
                                        if (propToUpdate != null) {
×
1271
                                                propToUpdate.setDescription(coreProp.getDescription());
×
1272
                                                Context.getAdministrationService().saveGlobalProperty(propToUpdate);
×
1273
                                        }
1274
                                        // set missing datatypes
1275
                                        propToUpdate = propsMissingDatatype.get(corePropName);
×
1276
                                        if (propToUpdate != null && coreProp.getDatatypeClassname() != null) {
×
1277
                                                propToUpdate.setDatatypeClassname(coreProp.getDatatypeClassname());
×
1278
                                                propToUpdate.setDatatypeConfig(coreProp.getDatatypeConfig());
×
1279
                                                propToUpdate.setPreferredHandlerClassname(coreProp.getPreferredHandlerClassname());
×
1280
                                                propToUpdate.setHandlerConfig(coreProp.getHandlerConfig());
×
1281
                                                Context.getAdministrationService().saveGlobalProperty(propToUpdate);
×
1282
                                        }
1283
                                }
1284
                        }
×
1285
                }
1286
                catch (Exception e) {
×
1287
                        log.error("Error while setting core global properties", e);
×
1288
                }
1289
                finally {
1290
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES);
×
1291
                        Context.removeProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
1292
                }
1293

1294
                // setting default validation rule
1295
                AdministrationService as = Context.getAdministrationService();
×
1296
                Boolean disableValidation = Boolean.valueOf(as.getGlobalProperty(OpenmrsConstants.GP_DISABLE_VALIDATION, "false"));
×
1297
                ValidateUtil.setDisableValidation(disableValidation);
×
1298

1299
                PersonName.setFormat(Context.getAdministrationService().getGlobalProperty(
×
1300
                                OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_FORMAT));
1301

1302
                Allergen.setOtherNonCodedConceptUuid(Context.getAdministrationService().getGlobalProperty(
×
1303
                                OpenmrsConstants.GP_ALLERGEN_OTHER_NON_CODED_UUID));
1304
        }
×
1305

1306
        /**
1307
         * Runs any needed updates on the current database if the user has the allow_auto_update runtime
1308
         * property set to true. If not set to true, then {@link #updateDatabase(Map)} must be called.<br>
1309
         * <br>
1310
         * If an {@link InputRequiredException} is thrown, a call to {@link #updateDatabase(Map)} is
1311
         * required with a mapping from question prompt to user answer.
1312
         *
1313
         * @param props the runtime properties
1314
         * @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
1315
         *             cannot continue without input from the user
1316
         * @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
1317
         *      the required question/datatypes
1318
         */
1319
        private static void checkForDatabaseUpdates(Properties props) throws DatabaseUpdateException, InputRequiredException {
1320
                boolean updatesRequired;
1321
                try {
1322
                        updatesRequired = DatabaseUpdater.updatesRequired();
×
1323
                }
1324
                catch (Exception e) {
×
1325
                        throw new DatabaseUpdateException("Unable to check if database updates are required", e);
×
1326
                }
×
1327

1328
                // this must be the first thing run in case it changes database mappings
1329
                if (updatesRequired) {
×
1330
                        if (DatabaseUpdater.allowAutoUpdate()) {
×
1331
                                DatabaseUpdater.executeChangelog();
×
1332
                        } else {
1333
                                throw new DatabaseUpdateException(
×
1334
                                                "Database updates are required.  Call Context.updateDatabase() before .startup() to continue.");
1335
                        }
1336
                }
1337
        }
×
1338

1339
        /**
1340
         * Updates the openmrs database to the latest. This is only needed if using the API alone. <br>
1341
         * <br>
1342
         * The typical use-case would be: Try to {@link #startup(String, String, String, Properties)},
1343
         * if that fails, call this method to get the database up to speed.
1344
         *
1345
         * @param userInput (can be null) responses from the user about needed input
1346
         * @throws DatabaseUpdateException if an error occurred while updating
1347
         * @since 1.5
1348
         * @deprecated as of 2.4
1349
         * 
1350
         */
1351
        @Deprecated
1352
        public static void updateDatabase(Map<String, Object> userInput) throws DatabaseUpdateException {
1353
                throw new UnsupportedOperationException("As of 2.4, this method is not longer implemented");
×
1354
        }
1355

1356
        /**
1357
         * Gets the simple date format for the current user's locale. The format will be similar in size
1358
         * to mm/dd/yyyy
1359
         *
1360
         * @return SimpleDateFormat for the user's current locale
1361
         * @see org.openmrs.util.OpenmrsUtil#getDateFormat(Locale)
1362
         * <strong>Should</strong> return a pattern with four y characters in it
1363
         */
1364
        public static SimpleDateFormat getDateFormat() {
1365
                return OpenmrsUtil.getDateFormat(getLocale());
1✔
1366
        }
1367

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

1380
        /**
1381
         * Gets the simple datetime format for the current user's locale. The format will be similar to
1382
         * mm/dd/yyyy hh:mm a
1383
         *
1384
         * @return SimpleDateFormat for the user's current locale
1385
         * @see org.openmrs.util.OpenmrsUtil#getDateTimeFormat(Locale)
1386
         * <strong>Should</strong> return a pattern with four y characters and two h characters in it
1387
         */
1388
        public static SimpleDateFormat getDateTimeFormat() {
1389
                return OpenmrsUtil.getDateTimeFormat(getLocale());
1✔
1390
        }
1391

1392
        /**
1393
         * @return true/false whether the service context is currently being refreshed
1394
         * @see org.openmrs.api.context.ServiceContext#isRefreshingContext()
1395
         */
1396
        public static boolean isRefreshingContext() {
1397
                return getServiceContext().isRefreshingContext();
1✔
1398
        }
1399

1400
        /**
1401
         * @since 1.5
1402
         * @see ServiceContext#getRegisteredComponents(Class)
1403
         */
1404
        public static <T> List<T> getRegisteredComponents(Class<T> type) {
1405
                return getServiceContext().getRegisteredComponents(type);
1✔
1406
        }
1407

1408
        /**
1409
         * @see ServiceContext#getRegisteredComponent(String, Class)
1410
         * @since 1.9.4
1411
         */
1412
        public static <T> T getRegisteredComponent(String beanName, Class<T> type) throws APIException {
1413
                return getServiceContext().getRegisteredComponent(beanName, type);
1✔
1414
        }
1415

1416
        /**
1417
         * @see ServiceContext#getModuleOpenmrsServices(String)
1418
         * @since 1.9
1419
         */
1420
        public static List<OpenmrsService> getModuleOpenmrsServices(String modulePackage) {
1421
                return getServiceContext().getModuleOpenmrsServices(modulePackage);
1✔
1422
        }
1423

1424
        /**
1425
         * @since 1.9
1426
         * @see ServiceContext#getVisitService()
1427
         */
1428
        public static VisitService getVisitService() {
1429
                return getServiceContext().getVisitService();
1✔
1430
        }
1431

1432
        /**
1433
         * @since 1.9
1434
         * @see ServiceContext#getProviderService()
1435
         */
1436
        public static ProviderService getProviderService() {
1437
                return getServiceContext().getProviderService();
1✔
1438
        }
1439

1440
        /**
1441
         * @since 1.9
1442
         * @see ServiceContext#getDatatypeService()
1443
         */
1444
        public static DatatypeService getDatatypeService() {
1445
                return getServiceContext().getDatatypeService();
1✔
1446
        }
1447

1448
        /**
1449
         * Add or replace a property in the config properties list
1450
         *
1451
         * @param key name of the property
1452
         * @param value value of the property
1453
         * @since 1.9
1454
         */
1455
        public static void addConfigProperty(Object key, Object value) {
1456
                configProperties.put(key, value);
×
1457
        }
×
1458

1459
        /**
1460
         * Remove a property from the list of config properties
1461
         *
1462
         * @param key name of the property
1463
         * @since 1.9
1464
         */
1465
        public static void removeConfigProperty(Object key) {
1466
                configProperties.remove(key);
×
1467
        }
×
1468

1469
        /**
1470
         * Get the config properties that have been added to this OpenMRS instance
1471
         *
1472
         * @return copy of the module properties
1473
         * @since 1.9
1474
         */
1475
        public static Properties getConfigProperties() {
1476
                Properties props = new Properties();
1✔
1477
                props.putAll(configProperties);
1✔
1478
                return props;
1✔
1479
        }
1480

1481
        /**
1482
         * Updates the search index. It is a blocking operation, which may take even a few minutes
1483
         * depending on the index size.
1484
         * <p>
1485
         * There is no need to call this method in normal usage since the index is automatically updated
1486
         * whenever DB transactions are committed.
1487
         * <p>
1488
         * The method is designated to be used in tests, which rollback transactions. Note that if the
1489
         * transaction is rolled back, changes to the index will not be reverted.
1490
         *
1491
         * @since 1.11
1492
         */
1493
        public static void updateSearchIndex() {
1494
                getContextDAO().updateSearchIndex();
×
1495
        }
×
1496

1497
        /**
1498
         * Updates the search index. It is an asynchronous operation.
1499
         * <p>
1500
         * There is no need to call this method in normal usage since the index is automatically updated
1501
         * whenever DB transactions are committed.
1502
         * <p>
1503
         *
1504
         * @return object representing the result of the started asynchronous operation
1505
         */
1506
        public static Future<?> updateSearchIndexAsync() {
1507
                return getContextDAO().updateSearchIndexAsync();
×
1508
        }
1509

1510
        /**
1511
         * It should be used <b>IN TESTS ONLY</b>. See {@link #updateSearchIndex(Class[])} for normal use.
1512
         * <p>
1513
         * Updates the search index for objects of the given type.
1514
         *
1515
         * @see #updateSearchIndex()
1516
         * @see #updateSearchIndex(Class[]) 
1517
         * @param type
1518
         * @since 1.11
1519
         */
1520
        public static void updateSearchIndexForType(Class<?> type) {
1521
                getContextDAO().updateSearchIndexForType(type);
1✔
1522
        }
1✔
1523

1524
        /**
1525
         * Updates the search index for objects of the given types using mass indexer.
1526
         * 
1527
         * @see #updateSearchIndex() 
1528
         * @param types
1529
         * @since 2.8.0
1530
         */
1531
        public static void updateSearchIndex(Class<?>... types) {
1532
                getContextDAO().updateSearchIndex(types);
×
1533
        }
×
1534

1535
        /**
1536
         * Updates the search index for the given object.
1537
         *
1538
         * @see #updateSearchIndex()
1539
         * @param object
1540
         * @since 1.11
1541
         */
1542
        public static void updateSearchIndexForObject(Object object) {
1543
                getContextDAO().updateSearchIndexForObject(object);
×
1544
        }
×
1545

1546
        /**
1547
         * @see org.openmrs.api.context.ServiceContext#setUseSystemClassLoader(boolean)
1548
         * @since 1.10
1549
         */
1550
        public static void setUseSystemClassLoader(boolean useSystemClassLoader) {
1551
                getServiceContext().setUseSystemClassLoader(useSystemClassLoader);
1✔
1552
        }
1✔
1553

1554
        /**
1555
         * @see org.openmrs.api.context.ServiceContext#isUseSystemClassLoader()
1556
         * @since 1.10
1557
         */
1558
        public static boolean isUseSystemClassLoader() {
1559
                return getServiceContext().isUseSystemClassLoader();
×
1560
        }
1561

1562
        /**
1563
         * @return a Connection from the OpenMRS database connection pool
1564
         * @since 2.5.7
1565
         */
1566
        public static Connection getDatabaseConnection() {
1567
                return getContextDAO().getDatabaseConnection();
×
1568
        }
1569
}
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