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

openmrs / openmrs-core / 14752386992

30 Apr 2025 10:25AM UTC coverage: 64.96% (-0.1%) from 65.095%
14752386992

push

github

web-flow
TRUNK-6316 Upgrade Hibernate Search to 6.2.4 (#5005)

501 of 591 new or added lines in 24 files covered. (84.77%)

21 existing lines in 6 files now uncovered.

23344 of 35936 relevant lines covered (64.96%)

0.65 hits per line

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

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

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

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

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

141
        // Global resources
142
        private static ContextDAO contextDAO;
143

144
        private static Session mailSession;
145

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

150
        private static volatile ServiceContext serviceContext;
151

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

154
        private static Properties configProperties = new Properties();
1✔
155

156
        private static AuthenticationScheme authenticationScheme;
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) {
1✔
276
                                if (serviceContext == null) {
1✔
277
                                        log.info("Creating new service context");
1✔
278
                                        serviceContext = ServiceContext.getInstance();
1✔
279
                                }
280
                        }
1✔
281
                }
282
                log.trace("serviceContext: {}", serviceContext);
1✔
283

284
                return ServiceContext.getInstance();
1✔
285
        }
286

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

296
        public static void setContext(ServiceContext ctx) {
297
                serviceContext = ctx;
1✔
298
        }
1✔
299

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

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

331
        /**
332
         * @param credentials
333
         * @throws ContextAuthenticationException
334
         * 
335
         * @since 2.3.0
336
         */
337
        public static Authenticated authenticate(Credentials credentials) throws ContextAuthenticationException {
338

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

345
                if (credentials == null) {
1✔
346
                        throw new ContextAuthenticationException("Context cannot authenticate with null credentials.");
×
347
                }
348

349
                return getUserContext().authenticate(credentials);
1✔
350
        }
351

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

366
                getUserContext().refreshAuthenticatedUser();
1✔
367
        }
1✔
368

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

379
                getUserContext().becomeUser(systemId);
1✔
380
        }
1✔
381

382
        /**
383
         * Get the runtime properties that this OpenMRS instance was started with
384
         *
385
         * @return copy of the runtime properties
386
         */
387
        public static Properties getRuntimeProperties() {
388
                log.trace("getting runtime properties. size: {}", runtimeProperties.size());
1✔
389

390
                Properties props = new Properties();
1✔
391
                props.putAll(runtimeProperties);
1✔
392

393
                return props;
1✔
394
        }
395

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

405
        /**
406
         * @return concept dictionary-related services
407
         */
408
        public static ConceptService getConceptService() {
409
                return getServiceContext().getConceptService();
1✔
410
        }
411

412
        /**
413
         * @return encounter-related services
414
         */
415
        public static EncounterService getEncounterService() {
416
                return getServiceContext().getEncounterService();
1✔
417
        }
418

419
        /**
420
         * @return location services
421
         */
422
        public static LocationService getLocationService() {
423
                return getServiceContext().getLocationService();
1✔
424
        }
425

426
        /**
427
         * @return observation services
428
         */
429
        public static ObsService getObsService() {
430
                return getServiceContext().getObsService();
1✔
431
        }
432

433
        /**
434
         * @return patient-related services
435
         */
436
        public static PatientService getPatientService() {
437
                return getServiceContext().getPatientService();
1✔
438
        }
439

440
        public static CohortService getCohortService() {
441
                return getServiceContext().getCohortService();
1✔
442
        }
443

444
        /**
445
         * @return person-related services
446
         */
447
        public static PersonService getPersonService() {
448
                return getServiceContext().getPersonService();
1✔
449
        }
450

451
        /**
452
         * @return condition-related services
453
         * 
454
         * @since 2.2
455
         */
456
        public static ConditionService getConditionService(){
457
                return getServiceContext().getConditionService();
1✔
458
        }
459

460
        /**
461
         * @return diagnosis-related services
462
         *
463
         * @since 2.2
464
         */
465
        public static DiagnosisService getDiagnosisService(){
466
                return getServiceContext().getDiagnosisService();
1✔
467
        }
468

469
        /**
470
         * @return MedicationDispense-related service
471
         * @since 2.6.0
472
         */
473
        public static MedicationDispenseService getMedicationDispenseService(){
474
                return getServiceContext().getMedicationDispenseService();
×
475
        }
476

477
        /**
478
         * @return Returns the hl7Service.
479
         */
480
        public static HL7Service getHL7Service() {
481
                return getServiceContext().getHL7Service();
1✔
482
        }
483

484
        /**
485
         * @return user-related services
486
         */
487
        public static UserService getUserService() {
488
                return getServiceContext().getUserService();
1✔
489
        }
490

491
        /**
492
         * @return order service
493
         */
494
        public static OrderService getOrderService() {
495
                return getServiceContext().getOrderService();
1✔
496
        }
497

498
        /**
499
         * @return orderSet service
500
         * @since 1.12
501
         */
502
        public static OrderSetService getOrderSetService() {
503
                return getServiceContext().getOrderSetService();
1✔
504
        }
505

506
        /**
507
         * @return form service
508
         */
509
        public static FormService getFormService() {
510
                return getServiceContext().getFormService();
1✔
511
        }
512

513
        /**
514
         * @return serialization service
515
         * @since 1.5
516
         */
517
        public static SerializationService getSerializationService() {
518
                return getServiceContext().getSerializationService();
1✔
519
        }
520

521
        /**
522
         * @return logic service
523
         */
524
        public static LogicService getLogicService() {
525
                return getServiceContext().getLogicService();
×
526
        }
527

528
        /**
529
         * @return admin-related services
530
         */
531
        public static AdministrationService getAdministrationService() {
532
                return getServiceContext().getAdministrationService();
1✔
533
        }
534

535
        /**
536
         * @return MessageSourceService
537
         */
538
        public static MessageSourceService getMessageSourceService() {
539
                return getServiceContext().getMessageSourceService();
1✔
540
        }
541

542
        /**
543
         * @return scheduler service
544
         */
545
        public static SchedulerService getSchedulerService() {
546
                return getServiceContext().getSchedulerService();
1✔
547
        }
548

549
        /**
550
         * @return alert service
551
         */
552
        public static AlertService getAlertService() {
553
                return getServiceContext().getAlertService();
1✔
554
        }
555

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

576
                        if (ms.getMessageSender() == null) {
1✔
577
                                ms.setMessageSender(getMessageSender());
1✔
578
                        }
579

580
                }
581
                catch (Exception e) {
×
582
                        log.error("Unable to create message service due", e);
×
583
                }
1✔
584
                return ms;
1✔
585
        }
586

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

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

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

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

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

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

682
                return getUserContext().getAuthenticatedUser();
1✔
683
        }
684

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

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

713
                getUserContext().logout();
1✔
714

715
                // reset the UserContext object (usually cleared out by closeSession()
716
                // soon after this)
717
                setUserContext(new UserContext(getAuthenticationScheme()));
1✔
718
        }
1✔
719

720
        /**
721
         * Convenience method. Passes through to userContext.getAllRoles(User)
722
         */
723
        public static Set<Role> getAllRoles(User user) throws Exception {
724
                return getUserContext().getAllRoles();
×
725
        }
726

727
        /**
728
         * Convenience method. Passes through to userContext.hasPrivilege(String)
729
         *
730
         * <strong>Should</strong> give daemon user full privileges
731
         */
732
        public static boolean hasPrivilege(String privilege) {
733
                // the daemon threads have access to all things
734
                if (Daemon.isDaemonThread()) {
1✔
735
                        return true;
1✔
736
                }
737

738
                return getUserContext().hasPrivilege(privilege);
1✔
739
        }
740

741
        /**
742
         * Throws an exception if the currently authenticated user does not have the specified
743
         * privilege.
744
         *
745
         * @param privilege
746
         * @throws ContextAuthenticationException
747
         */
748
        public static void requirePrivilege(String privilege) throws ContextAuthenticationException {
749
                if (!hasPrivilege(privilege)) {
1✔
750
                        String errorMessage;
751
                        if (StringUtils.isNotBlank(privilege)) {
×
752
                                errorMessage = Context.getMessageSourceService().getMessage("error.privilegesRequired",
×
753
                                                new Object[] { privilege }, null);
754
                        } else {
755
                                //Should we even be here if the privilege is blank?
756
                                errorMessage = Context.getMessageSourceService().getMessage("error.privilegesRequiredNoArgs");
×
757
                        }
758

759
                        throw new ContextAuthenticationException(errorMessage);
×
760
                }
761
        }
1✔
762

763
        /**
764
         * Convenience method. Passes through to {@link UserContext#addProxyPrivilege(String)}
765
         */
766
        public static void addProxyPrivilege(String privilege) {
767
                getUserContext().addProxyPrivilege(privilege);
1✔
768
        }
1✔
769

770
        /**
771
         * Convenience method. Passes through to {@link UserContext#removeProxyPrivilege(String)}
772
         */
773
        public static void removeProxyPrivilege(String privilege) {
774
                getUserContext().removeProxyPrivilege(privilege);
1✔
775
        }
1✔
776

777
        /**
778
         * Convenience method. Passes through to {@link UserContext#setLocale(Locale)}
779
         */
780
        public static void setLocale(Locale locale) {
781
                getUserContext().setLocale(locale);
1✔
782
        }
1✔
783

784
        /**
785
         * Convenience method. Passes through to {@link UserContext#getLocale()}
786
         *
787
         * <strong>Should</strong> not fail if session hasn't been opened
788
         */
789
        public static Locale getLocale() {
790
                // if a session hasn't been opened, just fetch the default
791
                if (!isSessionOpen()) {
1✔
792
                        return LocaleUtility.getDefaultLocale();
1✔
793
                }
794

795
                return getUserContext().getLocale();
1✔
796
        }
797

798
        /**
799
         * Used to define a unit of work. All "units of work" should be surrounded by openSession and
800
         * closeSession calls.
801
         */
802
        public static void openSession() {
803
                log.trace("opening session");
1✔
804
                setUserContext(new UserContext(getAuthenticationScheme())); // must be cleared out in
1✔
805
                // closeSession()
806
                getContextDAO().openSession();
1✔
807
        }
1✔
808

809
        /**
810
         * Used to define a unit of work. All "units of work" should be surrounded by openSession and
811
         * closeSession calls.
812
         */
813
        public static void closeSession() {
814
                log.trace("closing session");
1✔
815
                clearUserContext(); // because we set a UserContext on the current
1✔
816
                // thread in openSession()
817
                getContextDAO().closeSession();
1✔
818
        }
1✔
819

820
        /**
821
         * Used to define a unit of work which does not require clearing out the currently authenticated
822
         * user. Remember to call closeSessionWithCurrentUser in a, preferably, finally block after this
823
         * work.
824
         *
825
         * @since 1.10
826
         */
827
        public static void openSessionWithCurrentUser() {
828
                getContextDAO().openSession();
×
829
        }
×
830

831
        /**
832
         * Used when the a unit of work which started with a call for openSessionWithCurrentUser has
833
         * finished. This should be in a, preferably, finally block.
834
         *
835
         * @since 1.10
836
         */
837
        public static void closeSessionWithCurrentUser() {
838
                getContextDAO().closeSession();
×
839
        }
×
840

841
        /**
842
         * Clears cached changes made so far during this unit of work without writing them to the
843
         * database. If you call this method, and later call closeSession() or flushSession() your
844
         * changes are still lost.
845
         */
846
        public static void clearSession() {
847
                log.trace("clearing session");
1✔
848
                getContextDAO().clearSession();
1✔
849
        }
1✔
850

851
        /**
852
         * Forces any changes made so far in this unit of work to be written to the database
853
         *
854
         * @since 1.6
855
         */
856
        public static void flushSession() {
857
                log.trace("flushing session");
1✔
858
                getContextDAO().flushSession();
1✔
859
        }
1✔
860

861
        /**
862
         * This method tells whether {@link #openSession()} has been called or not already. If it hasn't
863
         * been called, some methods won't work correctly because a {@link UserContext} isn't available.
864
         *
865
         * @return true if {@link #openSession()} has been called already.
866
         * @since 1.5
867
         * <strong>Should</strong> return true if session is closed
868
         */
869
        public static boolean isSessionOpen() {
870
                return userContextHolder.get() != null;
1✔
871
        }
872

873
        /**
874
         * Used to re-read the state of the given instance from the underlying database.
875
         * @since 2.0
876
         * @param obj The object to refresh from the database in the session
877
         */
878
        public static void refreshEntity(Object obj) {
879
                log.trace("refreshing object: {}", obj);
1✔
880
                getContextDAO().refreshEntity(obj);
1✔
881
        }
1✔
882

883
        /**
884
         * Used to clear a cached object out of a session in the middle of a unit of work. Future
885
         * updates to this object will not be saved. Future gets of this object will not fetch this
886
         * cached copy
887
         *
888
         * @param obj The object to evict/remove from the session
889
         */
890
        public static void evictFromSession(Object obj) {
891
                log.trace("clearing session");
1✔
892
                getContextDAO().evictFromSession(obj);
1✔
893
        }
1✔
894

895
        /**
896
         * Evicts the entity data for a particular entity instance.
897
         *
898
         * @param object entity instance to evict from the DB cache
899
         */
900
        public static void evictEntity(OpenmrsObject object) {
901
                log.debug("Clearing DB cache for entity: {} with id: {}", object.getClass(), object.getId());
1✔
902
                getContextDAO().evictEntity(object);
1✔
903
        }
1✔
904
        
905
        /**
906
         * Evicts all entity data of a particular class from the given region.
907
         * 
908
         * @param entityClass entity class to evict from the DB cache
909
         */
910
        public static void evictAllEntities(Class<?> entityClass) {
911
                log.debug("Clearing DB cache for entities of type: {}", entityClass);
1✔
912
                getContextDAO().evictAllEntities(entityClass);
1✔
913
        }
1✔
914
        
915
        /**
916
         * Evicts data from all cache regions.
917
         */
918
        public static void clearEntireCache() {
919
                log.debug("Clearing DB cache from all regions");
1✔
920
                getContextDAO().clearEntireCache();
1✔
921
        }
1✔
922
        
923
        /**
924
         * Starts the OpenMRS System Should be called prior to any kind of activity
925
         *
926
         * @param props Runtime properties to use for startup
927
         * @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
928
         *             cannot continue without input from the user
929
         * @throws DatabaseUpdateException if database updates are required, see
930
         *             {@link DatabaseUpdater#executeChangelog()}
931
         * @throws ModuleMustStartException if a module that should be started is not able to
932
         * @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
933
         *      the required question/datatypes
934
         */
935
        public static synchronized void startup(Properties props) throws DatabaseUpdateException, InputRequiredException,
936
        ModuleMustStartException {
937
                // do any context database specific startup
938
                getContextDAO().startup(props);
×
939

940
                // find/set/check whether the current database version is compatible
941
                checkForDatabaseUpdates(props);
×
942

943
                // this should be first in the startup routines so that the application
944
                // data directory can be set from the runtime properties
945
                OpenmrsUtil.startup(props);
×
946

947
                openSession();
×
948
                clearSession();
×
949

950
                // add any privileges/roles that /must/ exist for openmrs to work
951
                // correctly.
952
                checkCoreDataset();
×
953

954
                getContextDAO().setupSearchIndex();
×
955

956
                // Loop over each module and startup each with these custom properties
957
                ModuleUtil.startup(props);
×
958
        }
×
959

960
        /**
961
         * Starts the OpenMRS System in a _non-webapp_ environment<br>
962
         * <br>
963
         * <b>Note:</b> This method calls {@link Context#openSession()}, so you must call
964
         * {@link Context#closeSession()} somewhere on the same thread of this application so as to not
965
         * leak memory.
966
         *
967
         * @param url database url like "jdbc:mysql://localhost:3306/openmrs?autoReconnect=true"
968
         * @param username Connection username
969
         * @param password Connection password
970
         * @param properties Other startup properties
971
         * @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
972
         *             cannot continue without input from the user
973
         * @throws DatabaseUpdateException if the database must be updated. See {@link DatabaseUpdater}
974
         * @throws ModuleMustStartException if a module that should start is not able to
975
         * @see #startup(Properties)
976
         * @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
977
         *      the required question/datatypes
978
         */
979
        public static synchronized void startup(String url, String username, String password, Properties properties)
980
                        throws DatabaseUpdateException, InputRequiredException, ModuleMustStartException {
981
                if (properties == null) {
×
982
                        properties = new Properties();
×
983
                }
984

985
                properties.put("connection.url", url);
×
986
                properties.put("connection.username", username);
×
987
                properties.put("connection.password", password);
×
988
                setRuntimeProperties(properties);
×
989

990
                openSession(); // so that the startup method can use proxyPrivileges
×
991

992
                startup(properties);
×
993

994
                // start the scheduled tasks
995
                SchedulerUtil.startup(properties);
×
996

997
                closeSession();
×
998
        }
×
999

1000
        /**
1001
         * Stops the OpenMRS System Should be called after all activity has ended and application is
1002
         * closing
1003
         */
1004
        public static void shutdown() {
1005
                log.debug("Shutting down the scheduler");
×
1006
                try {
1007
                        // Needs to be shutdown before Hibernate
1008
                        SchedulerUtil.shutdown();
×
1009
                }
1010
                catch (Exception e) {
×
1011
                        log.warn("Error while shutting down scheduler service", e);
×
1012
                }
×
1013

1014
                log.debug("Shutting down the modules");
×
1015
                try {
1016
                        ModuleUtil.shutdown();
×
1017
                }
1018
                catch (Exception e) {
×
1019
                        log.warn("Error while shutting down module system", e);
×
1020
                }
×
1021

1022
                log.debug("Shutting down the context");
×
1023
                try {
1024
                        ContextDAO dao = null;
×
1025
                        try {
1026
                                dao = getContextDAO();
×
1027
                        }
1028
                        catch (APIException e) {
×
1029
                                // pass
1030
                        }
×
1031
                        if (dao != null) {
×
1032
                                dao.shutdown();
×
1033
                        }
1034
                }
1035
                catch (Exception e) {
×
1036
                        log.warn("Error while shutting down context dao", e);
×
1037
                }
×
1038
        }
×
1039

1040
        /**
1041
         * Used for getting services not in the previous get*Service() calls
1042
         *
1043
         * @param cls The Class of the service to get
1044
         * @return The requested Service
1045
         * <strong>Should</strong> return the same object when called multiple times for the same class
1046
         */
1047
        public static <T> T getService(Class<? extends T> cls) {
1048
                return getServiceContext().getService(cls);
1✔
1049
        }
1050

1051
        /**
1052
         * Adds an AOP advisor around the given Class <code>cls</code>
1053
         * <p>
1054
         * Advisors can wrap around a method and effect the method before or after
1055
         *
1056
         * @param cls
1057
         * @param advisor
1058
         */
1059
        public static void addAdvisor(Class cls, Advisor advisor) {
1060
                getServiceContext().addAdvisor(cls, advisor);
×
1061
        }
×
1062

1063
        /**
1064
         * Adds an AOP advice object around the given Class <code>cls</code>
1065
         * <p>
1066
         * Advice comes in the form of before or afterReturning methods
1067
         *
1068
         * @param cls
1069
         * @param advice
1070
         */
1071
        public static void addAdvice(Class cls, Advice advice) {
1072
                getServiceContext().addAdvice(cls, advice);
×
1073
        }
×
1074

1075
        /**
1076
         * Removes the given AOP advisor from Class <code>cls</code>
1077
         *
1078
         * @param cls
1079
         * @param advisor
1080
         */
1081
        public static void removeAdvisor(Class cls, Advisor advisor) {
1082
                getServiceContext().removeAdvisor(cls, advisor);
×
1083
        }
×
1084

1085
        /**
1086
         * Removes the given AOP advice object from Class <code>cls</code>
1087
         *
1088
         * @param cls
1089
         * @param advice
1090
         */
1091
        public static void removeAdvice(Class cls, Advice advice) {
1092
                getServiceContext().removeAdvice(cls, advice);
×
1093
        }
×
1094

1095
        /**
1096
         * Runs through the core data (e.g. privileges, roles, and global properties) and adds them if
1097
         * necessary.
1098
         */
1099
        public static void checkCoreDataset() {
1100
                // setting core roles
1101
                try {
1102
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_ROLES);
×
1103
                        Set<String> currentRoleNames = new HashSet<>();
×
1104
                        for (Role role : Context.getUserService().getAllRoles()) {
×
1105
                                currentRoleNames.add(role.getRole().toUpperCase());
×
1106
                        }
×
1107
                        Map<String, String> map = OpenmrsUtil.getCoreRoles();
×
1108
                        for (Map.Entry<String, String> entry : map.entrySet()) {
×
1109
                                String roleName = entry.getKey();
×
1110
                                if (!currentRoleNames.contains(roleName.toUpperCase())) {
×
1111
                                        Role role = new Role();
×
1112
                                        role.setRole(roleName);
×
1113
                                        role.setDescription(entry.getValue());
×
1114
                                        Context.getUserService().saveRole(role);
×
1115
                                }
1116
                        }
×
1117
                }
1118
                catch (Exception e) {
×
1119
                        log.error("Error while setting core roles for openmrs system", e);
×
1120
                }
1121
                finally {
1122
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_ROLES);
×
1123
                }
1124

1125
                // setting core privileges
1126
                try {
1127
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_PRIVILEGES);
×
1128
                        Set<String> currentPrivilegeNames = new HashSet<>();
×
1129
                        for (Privilege privilege : Context.getUserService().getAllPrivileges()) {
×
1130
                                currentPrivilegeNames.add(privilege.getPrivilege().toUpperCase());
×
1131
                        }
×
1132
                        Map<String, String> map = OpenmrsUtil.getCorePrivileges();
×
1133
                        for (Map.Entry<String, String> entry : map.entrySet()) {
×
1134
                                String privilegeName = entry.getKey();
×
1135
                                if (!currentPrivilegeNames.contains(privilegeName.toUpperCase())) {
×
1136
                                        Privilege p = new Privilege();
×
1137
                                        p.setPrivilege(privilegeName);
×
1138
                                        p.setDescription(entry.getValue());
×
1139
                                        Context.getUserService().savePrivilege(p);
×
1140
                                }
1141
                        }
×
1142
                }
1143
                catch (Exception e) {
×
1144
                        log.error("Error while setting core privileges", e);
×
1145
                }
1146
                finally {
1147
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_PRIVILEGES);
×
1148
                }
1149

1150
                // setting core global properties
1151
                try {
1152
                        Context.addProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES);
×
1153
                        Context.addProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
1154
                        Set<String> currentPropNames = new HashSet<>();
×
1155
                        Map<String, GlobalProperty> propsMissingDescription = new HashMap<>();
×
1156
                        Map<String, GlobalProperty> propsMissingDatatype = new HashMap<>();
×
1157
                        for (GlobalProperty prop : Context.getAdministrationService().getAllGlobalProperties()) {
×
1158
                                currentPropNames.add(prop.getProperty().toUpperCase());
×
1159
                                if (prop.getDescription() == null) {
×
1160
                                        propsMissingDescription.put(prop.getProperty().toUpperCase(), prop);
×
1161
                                }
1162
                                if (prop.getDatatypeClassname() == null) {
×
1163
                                        propsMissingDatatype.put(prop.getProperty().toUpperCase(), prop);
×
1164
                                }
1165
                        }
×
1166

1167
                        for (GlobalProperty coreProp : OpenmrsConstants.CORE_GLOBAL_PROPERTIES()) {
×
1168
                                String corePropName = coreProp.getProperty().toUpperCase();
×
1169
                                // if the prop doesn't exist, save it
1170
                                if (!currentPropNames.contains(corePropName)) {
×
1171
                                        Context.getAdministrationService().saveGlobalProperty(coreProp);
×
1172
                                        currentPropNames.add(corePropName); // add to list in case
×
1173
                                        // of duplicates
1174
                                } else {
1175
                                        // if the prop is missing its description, update it
1176
                                        GlobalProperty propToUpdate = propsMissingDescription.get(corePropName);
×
1177
                                        if (propToUpdate != null) {
×
1178
                                                propToUpdate.setDescription(coreProp.getDescription());
×
1179
                                                Context.getAdministrationService().saveGlobalProperty(propToUpdate);
×
1180
                                        }
1181
                                        // set missing datatypes
1182
                                        propToUpdate = propsMissingDatatype.get(corePropName);
×
1183
                                        if (propToUpdate != null && coreProp.getDatatypeClassname() != null) {
×
1184
                                                propToUpdate.setDatatypeClassname(coreProp.getDatatypeClassname());
×
1185
                                                propToUpdate.setDatatypeConfig(coreProp.getDatatypeConfig());
×
1186
                                                propToUpdate.setPreferredHandlerClassname(coreProp.getPreferredHandlerClassname());
×
1187
                                                propToUpdate.setHandlerConfig(coreProp.getHandlerConfig());
×
1188
                                                Context.getAdministrationService().saveGlobalProperty(propToUpdate);
×
1189
                                        }
1190
                                }
1191
                        }
×
1192
                }
1193
                catch (Exception e) {
×
1194
                        log.error("Error while setting core global properties", e);
×
1195
                }
1196
                finally {
1197
                        Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES);
×
1198
                        Context.removeProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
1199
                }
1200

1201
                // setting default validation rule
1202
                AdministrationService as = Context.getAdministrationService();
×
1203
                Boolean disableValidation = Boolean.valueOf(as.getGlobalProperty(OpenmrsConstants.GP_DISABLE_VALIDATION, "false"));
×
1204
                ValidateUtil.setDisableValidation(disableValidation);
×
1205

1206
                PersonName.setFormat(Context.getAdministrationService().getGlobalProperty(
×
1207
                                OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_FORMAT));
1208

1209
                Allergen.setOtherNonCodedConceptUuid(Context.getAdministrationService().getGlobalProperty(
×
1210
                                OpenmrsConstants.GP_ALLERGEN_OTHER_NON_CODED_UUID));
1211
        }
×
1212

1213
        /**
1214
         * Runs any needed updates on the current database if the user has the allow_auto_update runtime
1215
         * property set to true. If not set to true, then {@link #updateDatabase(Map)} must be called.<br>
1216
         * <br>
1217
         * If an {@link InputRequiredException} is thrown, a call to {@link #updateDatabase(Map)} is
1218
         * required with a mapping from question prompt to user answer.
1219
         *
1220
         * @param props the runtime properties
1221
         * @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
1222
         *             cannot continue without input from the user
1223
         * @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
1224
         *      the required question/datatypes
1225
         */
1226
        private static void checkForDatabaseUpdates(Properties props) throws DatabaseUpdateException, InputRequiredException {
1227
                boolean updatesRequired;
1228
                try {
1229
                        updatesRequired = DatabaseUpdater.updatesRequired();
×
1230
                }
1231
                catch (Exception e) {
×
1232
                        throw new DatabaseUpdateException("Unable to check if database updates are required", e);
×
1233
                }
×
1234

1235
                // this must be the first thing run in case it changes database mappings
1236
                if (updatesRequired) {
×
1237
                        if (DatabaseUpdater.allowAutoUpdate()) {
×
1238
                                DatabaseUpdater.executeChangelog();
×
1239
                        } else {
1240
                                throw new DatabaseUpdateException(
×
1241
                                                "Database updates are required.  Call Context.updateDatabase() before .startup() to continue.");
1242
                        }
1243
                }
1244
        }
×
1245

1246
        /**
1247
         * Updates the openmrs database to the latest. This is only needed if using the API alone. <br>
1248
         * <br>
1249
         * The typical use-case would be: Try to {@link #startup(String, String, String, Properties)},
1250
         * if that fails, call this method to get the database up to speed.
1251
         *
1252
         * @param userInput (can be null) responses from the user about needed input
1253
         * @throws DatabaseUpdateException if an error occurred while updating
1254
         * @since 1.5
1255
         * @deprecated as of 2.4
1256
         * 
1257
         */
1258
        @Deprecated
1259
        public static void updateDatabase(Map<String, Object> userInput) throws DatabaseUpdateException {
1260
                throw new UnsupportedOperationException("As of 2.4, this method is not longer implemented");
×
1261
        }
1262

1263
        /**
1264
         * Gets the simple date format for the current user's locale. The format will be similar in size
1265
         * to mm/dd/yyyy
1266
         *
1267
         * @return SimpleDateFormat for the user's current locale
1268
         * @see org.openmrs.util.OpenmrsUtil#getDateFormat(Locale)
1269
         * <strong>Should</strong> return a pattern with four y characters in it
1270
         */
1271
        public static SimpleDateFormat getDateFormat() {
1272
                return OpenmrsUtil.getDateFormat(getLocale());
1✔
1273
        }
1274

1275
        /**
1276
         * Gets the simple time format for the current user's locale. The format will be similar to
1277
         * hh:mm a
1278
         *
1279
         * @return SimpleDateFormat for the user's current locale
1280
         * @see org.openmrs.util.OpenmrsUtil#getTimeFormat(Locale)
1281
         * <strong>Should</strong> return a pattern with two h characters in it
1282
         */
1283
        public static SimpleDateFormat getTimeFormat() {
1284
                return OpenmrsUtil.getTimeFormat(getLocale());
×
1285
        }
1286

1287
        /**
1288
         * Gets the simple datetime format for the current user's locale. The format will be similar to
1289
         * mm/dd/yyyy hh:mm a
1290
         *
1291
         * @return SimpleDateFormat for the user's current locale
1292
         * @see org.openmrs.util.OpenmrsUtil#getDateTimeFormat(Locale)
1293
         * <strong>Should</strong> return a pattern with four y characters and two h characters in it
1294
         */
1295
        public static SimpleDateFormat getDateTimeFormat() {
1296
                return OpenmrsUtil.getDateTimeFormat(getLocale());
1✔
1297
        }
1298

1299
        /**
1300
         * @return true/false whether the service context is currently being refreshed
1301
         * @see org.openmrs.api.context.ServiceContext#isRefreshingContext()
1302
         */
1303
        public static boolean isRefreshingContext() {
1304
                return getServiceContext().isRefreshingContext();
1✔
1305
        }
1306

1307
        /**
1308
         * @since 1.5
1309
         * @see ServiceContext#getRegisteredComponents(Class)
1310
         */
1311
        public static <T> List<T> getRegisteredComponents(Class<T> type) {
1312
                return getServiceContext().getRegisteredComponents(type);
1✔
1313
        }
1314

1315
        /**
1316
         * @see ServiceContext#getRegisteredComponent(String, Class)
1317
         * @since 1.9.4
1318
         */
1319
        public static <T> T getRegisteredComponent(String beanName, Class<T> type) throws APIException {
1320
                return getServiceContext().getRegisteredComponent(beanName, type);
1✔
1321
        }
1322

1323
        /**
1324
         * @see ServiceContext#getModuleOpenmrsServices(String)
1325
         * @since 1.9
1326
         */
1327
        public static List<OpenmrsService> getModuleOpenmrsServices(String modulePackage) {
1328
                return getServiceContext().getModuleOpenmrsServices(modulePackage);
1✔
1329
        }
1330

1331
        /**
1332
         * @since 1.9
1333
         * @see ServiceContext#getVisitService()
1334
         */
1335
        public static VisitService getVisitService() {
1336
                return getServiceContext().getVisitService();
1✔
1337
        }
1338

1339
        /**
1340
         * @since 1.9
1341
         * @see ServiceContext#getProviderService()
1342
         */
1343
        public static ProviderService getProviderService() {
1344
                return getServiceContext().getProviderService();
1✔
1345
        }
1346

1347
        /**
1348
         * @since 1.9
1349
         * @see ServiceContext#getDatatypeService()
1350
         */
1351
        public static DatatypeService getDatatypeService() {
1352
                return getServiceContext().getDatatypeService();
1✔
1353
        }
1354

1355
        /**
1356
         * Add or replace a property in the config properties list
1357
         *
1358
         * @param key name of the property
1359
         * @param value value of the property
1360
         * @since 1.9
1361
         */
1362
        public static void addConfigProperty(Object key, Object value) {
1363
                configProperties.put(key, value);
×
1364
        }
×
1365

1366
        /**
1367
         * Remove a property from the list of config properties
1368
         *
1369
         * @param key name of the property
1370
         * @since 1.9
1371
         */
1372
        public static void removeConfigProperty(Object key) {
1373
                configProperties.remove(key);
×
1374
        }
×
1375

1376
        /**
1377
         * Get the config properties that have been added to this OpenMRS instance
1378
         *
1379
         * @return copy of the module properties
1380
         * @since 1.9
1381
         */
1382
        public static Properties getConfigProperties() {
1383
                Properties props = new Properties();
1✔
1384
                props.putAll(configProperties);
1✔
1385
                return props;
1✔
1386
        }
1387

1388
        /**
1389
         * Updates the search index. It is a blocking operation, which may take even a few minutes
1390
         * depending on the index size.
1391
         * <p>
1392
         * There is no need to call this method in normal usage since the index is automatically updated
1393
         * whenever DB transactions are committed.
1394
         * <p>
1395
         * The method is designated to be used in tests, which rollback transactions. Note that if the
1396
         * transaction is rolled back, changes to the index will not be reverted.
1397
         *
1398
         * @since 1.11
1399
         */
1400
        public static void updateSearchIndex() {
1401
                getContextDAO().updateSearchIndex();
×
1402
        }
×
1403

1404
        /**
1405
         * Updates the search index. It is an asynchronous operation.
1406
         * <p>
1407
         * There is no need to call this method in normal usage since the index is automatically updated
1408
         * whenever DB transactions are committed.
1409
         * <p>
1410
         *
1411
         * @return object representing the result of the started asynchronous operation
1412
         */
1413
        public static Future<?> updateSearchIndexAsync() {
1414
                return getContextDAO().updateSearchIndexAsync();
×
1415
        }
1416

1417
        /**
1418
         * It should be used <b>IN TESTS ONLY</b>. See {@link #updateSearchIndex(Class[])} for normal use.
1419
         * <p>
1420
         * Updates the search index for objects of the given type.
1421
         *
1422
         * @see #updateSearchIndex()
1423
         * @see #updateSearchIndex(Class[]) 
1424
         * @param type
1425
         * @since 1.11
1426
         */
1427
        public static void updateSearchIndexForType(Class<?> type) {
1428
                getContextDAO().updateSearchIndexForType(type);
1✔
1429
        }
1✔
1430

1431
        /**
1432
         * Updates the search index for objects of the given types using mass indexer.
1433
         * 
1434
         * @see #updateSearchIndex() 
1435
         * @param types
1436
         * @since 2.8.0
1437
         */
1438
        public static void updateSearchIndex(Class<?>... types) {
NEW
1439
                getContextDAO().updateSearchIndex(types);
×
NEW
1440
        }
×
1441

1442
        /**
1443
         * Updates the search index for the given object.
1444
         *
1445
         * @see #updateSearchIndex()
1446
         * @param object
1447
         * @since 1.11
1448
         */
1449
        public static void updateSearchIndexForObject(Object object) {
1450
                getContextDAO().updateSearchIndexForObject(object);
×
1451
        }
×
1452

1453
        /**
1454
         * @see org.openmrs.api.context.ServiceContext#setUseSystemClassLoader(boolean)
1455
         * @since 1.10
1456
         */
1457
        public static void setUseSystemClassLoader(boolean useSystemClassLoader) {
1458
                getServiceContext().setUseSystemClassLoader(useSystemClassLoader);
1✔
1459
        }
1✔
1460

1461
        /**
1462
         * @see org.openmrs.api.context.ServiceContext#isUseSystemClassLoader()
1463
         * @since 1.10
1464
         */
1465
        public static boolean isUseSystemClassLoader() {
1466
                return getServiceContext().isUseSystemClassLoader();
×
1467
        }
1468

1469
        /**
1470
         * @return a Connection from the OpenMRS database connection pool
1471
         * @since 2.5.7
1472
         */
1473
        public static Connection getDatabaseConnection() {
1474
                return getContextDAO().getDatabaseConnection();
×
1475
        }
1476
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc