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

openmrs / openmrs-core / 18188744794

02 Oct 2025 09:14AM UTC coverage: 65.24% (-0.08%) from 65.318%
18188744794

push

github

rkorytkowski
TRUNK-6436: Add logging to monitor startup performance

(cherry picked from commit fb43aba18)

2 of 29 new or added lines in 4 files covered. (6.9%)

28 existing lines in 10 files now uncovered.

23611 of 36191 relevant lines covered (65.24%)

0.65 hits per line

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

74.83
/api/src/main/java/org/openmrs/api/context/ServiceContext.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 java.util.ArrayList;
13
import java.util.Collections;
14
import java.util.HashMap;
15
import java.util.HashSet;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Map.Entry;
19
import java.util.Set;
20

21
import org.aopalliance.aop.Advice;
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.ServiceNotFoundException;
43
import org.openmrs.api.UserService;
44
import org.openmrs.api.VisitService;
45
import org.openmrs.hl7.HL7Service;
46
import org.openmrs.logic.LogicService;
47
import org.openmrs.messagesource.MessageSourceService;
48
import org.openmrs.messagesource.impl.DefaultMessageSourceServiceImpl;
49
import org.openmrs.notification.AlertService;
50
import org.openmrs.notification.MessageService;
51
import org.openmrs.scheduler.SchedulerService;
52
import org.openmrs.util.OpenmrsClassLoader;
53
import org.openmrs.util.OpenmrsThreadPoolHolder;
54
import org.slf4j.Logger;
55
import org.slf4j.LoggerFactory;
56
import org.springframework.aop.Advisor;
57
import org.springframework.aop.framework.Advised;
58
import org.springframework.aop.framework.ProxyFactory;
59
import org.springframework.beans.BeansException;
60
import org.springframework.cache.CacheManager;
61
import org.springframework.context.ApplicationContext;
62
import org.springframework.context.ApplicationContextAware;
63

64
/**
65
 * Represents an OpenMRS <code>Service Context</code>, which returns the services represented
66
 * throughout the system. <br>
67
 * <br>
68
 * This class should not be access directly, but rather used through the <code>Context</code> class. <br>
69
 * <br>
70
 * This class is essentially static and only one instance is kept because this is fairly
71
 * heavy-weight. Spring takes care of filling in the actual service implementations via dependency
72
 * injection. See the /metadata/api/spring/applicationContext-service.xml file. <br>
73
 * <br>
74
 * Module services are also accessed through this class. See {@link #getService(Class)}
75
 *
76
 * @see org.openmrs.api.context.Context
77
 */
78
public class ServiceContext implements ApplicationContextAware {
79
        
80
        private static final Logger log = LoggerFactory.getLogger(ServiceContext.class);
1✔
81

82
        private ApplicationContext applicationContext;
83
        
84
        private static boolean refreshingContext = false;
1✔
85
        
86
        private static final Object refreshingContextLock = new Object();
1✔
87
        
88
        /**
89
         * Static variable holding whether or not to use the system classloader. By default this is
90
         * false so the openmrs classloader is used instead
91
         */
92
        private boolean useSystemClassLoader = false;
1✔
93
        
94
        // Cached service objects
95
        Map<Class, Object> services = new HashMap<>();
1✔
96
        
97
        // Advisors added to services by this service
98
        Map<Class, Set<Advisor>> addedAdvisors = new HashMap<>();
1✔
99
        
100
        // Advice added to services by this service
101
        Map<Class, Set<Advice>> addedAdvice = new HashMap<>();
1✔
102
        
103
        /**
104
         * Services implementing the OpenmrsService interface for each module. The map is keyed by the
105
         * full class name including package.
106
         *
107
         * @since 1.9
108
         */
109
        Map<String, OpenmrsService> moduleOpenmrsServices = new HashMap<>();
1✔
110
        
111
        /**
112
         * The default constructor is private so as to keep only one instance per java vm.
113
         *
114
         * @see ServiceContext#getInstance()
115
         */
116
        private ServiceContext() {
1✔
117
                log.debug("Instantiating service context");
1✔
118
        }
1✔
119
        
120
        private static class ServiceContextHolder {
121

122
                private ServiceContextHolder() {
123
                }
124

125
                private static ServiceContext instance = null;
1✔
126
        }
127
        
128
        /**
129
         * There should only be one ServiceContext per openmrs (java virtual machine). This method
130
         * should be used when wanting to fetch the service context Note: The ServiceContext shouldn't
131
         * be used independently. All calls should go through the Context
132
         *
133
         * @return This VM's current ServiceContext.
134
         * @see org.openmrs.api.context.Context
135
         */
136
        public static ServiceContext getInstance() {
137
                if (ServiceContextHolder.instance == null) {
1✔
138
                        ServiceContextHolder.instance = new ServiceContext();
1✔
139
                }
140
                
141
                return ServiceContextHolder.instance;
1✔
142
        }
143
        
144
        /**
145
         * Null out the current instance of the ServiceContext. This should be used when modules are
146
         * refreshing (being added/removed) and/or openmrs is shutting down
147
         */
148
        public static void destroyInstance() {
149
                if (ServiceContextHolder.instance != null && ServiceContextHolder.instance.services != null) {
1✔
150
                        for (Map.Entry<Class, Object> entry : ServiceContextHolder.instance.services.entrySet()) {
1✔
151
                                log.debug("Service - {} : {}", entry.getKey().getName(), entry.getValue());
1✔
152
                        }
1✔
153
                        
154
                        // Remove advice and advisors that this service added
155
                        for (Class serviceClass : ServiceContextHolder.instance.services.keySet()) {
1✔
156
                                ServiceContextHolder.instance.removeAddedAOP(serviceClass);
1✔
157
                        }
1✔
158
                        
159
                        if (ServiceContextHolder.instance.services != null) {
1✔
160
                                ServiceContextHolder.instance.services.clear();
1✔
161
                                ServiceContextHolder.instance.services = null;
1✔
162
                        }
163
                        
164
                        if (ServiceContextHolder.instance.addedAdvisors != null) {
1✔
165
                                ServiceContextHolder.instance.addedAdvisors.clear();
1✔
166
                                ServiceContextHolder.instance.addedAdvisors = null;
1✔
167
                        }
168
                        
169
                        if (ServiceContextHolder.instance.addedAdvice != null) {
1✔
170
                                ServiceContextHolder.instance.addedAdvice.clear();
1✔
171
                                ServiceContextHolder.instance.addedAdvice = null;
1✔
172
                        }
173
                }
174
                
175
                if (ServiceContextHolder.instance != null) {
1✔
176
                        ServiceContextHolder.instance.applicationContext = null;
1✔
177
                        
178
                        if (ServiceContextHolder.instance.moduleOpenmrsServices != null) {
1✔
179
                                ServiceContextHolder.instance.moduleOpenmrsServices.clear();
1✔
180
                                ServiceContextHolder.instance.moduleOpenmrsServices = null;
1✔
181
                        }
182
                }
183
                log.debug("Destroying ServiceContext instance: {}", ServiceContextHolder.instance);
1✔
184
                ServiceContextHolder.instance = null;
1✔
185
        }
1✔
186
        
187
        /**
188
         * @return encounter-related services
189
         */
190
        public EncounterService getEncounterService() {
191
                return getService(EncounterService.class);
1✔
192
        }
193
        
194
        /**
195
         * @return location services
196
         */
197
        public LocationService getLocationService() {
198
                return getService(LocationService.class);
1✔
199
        }
200
        
201
        /**
202
         * @return observation services
203
         */
204
        public ObsService getObsService() {
205
                return getService(ObsService.class);
1✔
206
        }
207
        
208
        /**
209
         * @return condition related service
210
         * 
211
         * @since 2.2
212
         */
213
        public ConditionService getConditionService() {
214
                return getService(ConditionService.class);
1✔
215
        }
216
        
217
        /**
218
         * @param conditionService condition related service
219
         *            
220
         * @since 2.2   
221
         */
222
        public void setConditionService(ConditionService conditionService) {
223
                setService(ConditionService.class, conditionService);
1✔
224
        }
1✔
225

226
        /**
227
         * @return diagnosis related service
228
         *
229
         * @since 2.2
230
         */
231
        public DiagnosisService getDiagnosisService() {
232
                return getService(DiagnosisService.class);
1✔
233
        }
234

235
        /**
236
         * @param diagnosisService diagnosis related service
237
         *
238
         * @since 2.2
239
         */
240
        public void setDiagnosisService(DiagnosisService diagnosisService) {
241
                setService(DiagnosisService.class, diagnosisService);
1✔
242
        }
1✔
243

244
        /**
245
         * @return MedicationDispense related service
246
         * @since 2.6.0
247
         */
248
        public MedicationDispenseService getMedicationDispenseService() {
249
                return getService(MedicationDispenseService.class);
×
250
        }
251

252
        /**
253
         * @param medicationDispenseService MedicationDispense related service
254
         * @since 2.6.0
255
         */
256
        public void setMedicationDispenseService(MedicationDispenseService medicationDispenseService) {
257
                setService(MedicationDispenseService.class, medicationDispenseService);
1✔
258
        }
1✔
259
        
260
        /**
261
         * @return cohort related service
262
         */
263
        public CohortService getCohortService() {
264
                return getService(CohortService.class);
1✔
265
        }
266
        
267
        /**
268
         * @param cs cohort related service
269
         */
270
        public void setCohortService(CohortService cs) {
271
                setService(CohortService.class, cs);
1✔
272
        }
1✔
273
        
274
        /**
275
         * @return order set service
276
         */
277
        public OrderSetService getOrderSetService() {
278
                return getService(OrderSetService.class);
1✔
279
        }
280
        
281
        /**
282
         * @return order service
283
         */
284
        public OrderService getOrderService() {
285
                return getService(OrderService.class);
1✔
286
        }
287
        
288
        /**
289
         * @return form service
290
         */
291
        public FormService getFormService() {
292
                return getService(FormService.class);
1✔
293
        }
294
        
295
        /**
296
         * @return serialization service
297
         */
298
        public SerializationService getSerializationService() {
299
                return getService(SerializationService.class);
1✔
300
        }
301
        
302
        /**
303
         * @return admin-related services
304
         */
305
        public AdministrationService getAdministrationService() {
306
                return getService(AdministrationService.class);
1✔
307
        }
308
        
309
        /**
310
         * @return programWorkflowService
311
         */
312
        public ProgramWorkflowService getProgramWorkflowService() {
313
                return getService(ProgramWorkflowService.class);
1✔
314
        }
315

316
        /**
317
         * @return logicService
318
         */
319
        public LogicService getLogicService() {
320
                return getService(LogicService.class);
×
321
        }
322
        
323
        /**
324
         * @return scheduler service
325
         */
326
        public SchedulerService getSchedulerService() {
327
                return getService(SchedulerService.class);
1✔
328
        }
329
        
330
        /**
331
         * Set the scheduler service.
332
         *
333
         * @param schedulerService
334
         */
335
        public void setSchedulerService(SchedulerService schedulerService) {
336
                setService(SchedulerService.class, schedulerService);
1✔
337
        }
1✔
338
        
339
        /**
340
         * @return alert service
341
         */
342
        public AlertService getAlertService() {
343
                return getService(AlertService.class);
1✔
344
        }
345
        
346
        /**
347
         * @param alertService
348
         */
349
        public void setAlertService(AlertService alertService) {
350
                setService(AlertService.class, alertService);
1✔
351
        }
1✔
352
        
353
        /**
354
         * @param programWorkflowService
355
         */
356
        public void setProgramWorkflowService(ProgramWorkflowService programWorkflowService) {
357
                setService(ProgramWorkflowService.class, programWorkflowService);
1✔
358
        }
1✔
359

360
        /**
361
         * @param logicService
362
         */
363
        public void setLogicService(LogicService logicService) {
364
                setService(LogicService.class, logicService);
×
365
        }
×
366
        
367
        /**
368
         * @return message service
369
         */
370
        public MessageService getMessageService() {
371
                return getService(MessageService.class);
1✔
372
        }
373
        
374
        /**
375
         * Sets the message service.
376
         *
377
         * @param messageService
378
         */
379
        public void setMessageService(MessageService messageService) {
380
                setService(MessageService.class, messageService);
1✔
381
        }
1✔
382
        
383
        /**
384
         * @return the hl7Service
385
         */
386
        public HL7Service getHL7Service() {
387
                return getService(HL7Service.class);
1✔
388
        }
389
        
390
        /**
391
         * @param hl7Service the hl7Service to set
392
         */
393
        public void setHl7Service(HL7Service hl7Service) {
394
                setService(HL7Service.class, hl7Service);
1✔
395
        }
1✔
396
        
397
        /**
398
         * @param administrationService the administrationService to set
399
         */
400
        public void setAdministrationService(AdministrationService administrationService) {
401
                setService(AdministrationService.class, administrationService);
1✔
402
        }
1✔
403
        
404
        /**
405
         * @param encounterService the encounterService to set
406
         */
407
        public void setEncounterService(EncounterService encounterService) {
408
                setService(EncounterService.class, encounterService);
1✔
409
        }
1✔
410
        
411
        /**
412
         * @param locationService the LocationService to set
413
         */
414
        public void setLocationService(LocationService locationService) {
415
                setService(LocationService.class, locationService);
1✔
416
        }
1✔
417
        
418
        /**
419
         * @param formService the formService to set
420
         */
421
        public void setFormService(FormService formService) {
422
                setService(FormService.class, formService);
1✔
423
        }
1✔
424
        
425
        /**
426
         * @param obsService the obsService to set
427
         */
428
        public void setObsService(ObsService obsService) {
429
                setService(ObsService.class, obsService);
1✔
430
        }
1✔
431

432
        /**
433
         * @param orderService the orderService to set
434
         */
435
        public void setOrderService(OrderService orderService) {
436
                setService(OrderService.class, orderService);
1✔
437
        }
1✔
438
        
439
        /**
440
         * @param orderSetService the orderSetService to set
441
         */
442
        public void setOrderSetService(OrderSetService orderSetService) {
443
                setService(OrderSetService.class, orderSetService);
1✔
444
        }
1✔
445
        
446
        /**
447
         * @param serializationService
448
         */
449
        public void setSerializationService(SerializationService serializationService) {
450
                setService(SerializationService.class, serializationService);
1✔
451
        }
1✔
452
        
453
        /**
454
         * @return patient related services
455
         */
456
        public PatientService getPatientService() {
457
                return getService(PatientService.class);
1✔
458
        }
459
        
460
        /**
461
         * @param patientService the patientService to set
462
         */
463
        public void setPatientService(PatientService patientService) {
464
                setService(PatientService.class, patientService);
1✔
465
        }
1✔
466
        
467
        /**
468
         * @return person related services
469
         */
470
        public PersonService getPersonService() {
471
                return getService(PersonService.class);
1✔
472
        }
473
        
474
        /**
475
         * @param personService the personService to set
476
         */
477
        public void setPersonService(PersonService personService) {
478
                setService(PersonService.class, personService);
1✔
479
        }
1✔
480
        
481
        /**
482
         * @return concept related services
483
         */
484
        public ConceptService getConceptService() {
485
                return getService(ConceptService.class);
1✔
486
        }
487
        
488
        /**
489
         * @param conceptService the conceptService to set
490
         */
491
        public void setConceptService(ConceptService conceptService) {
492
                setService(ConceptService.class, conceptService);
1✔
493
        }
1✔
494
        
495
        /**
496
         * @return user-related services
497
         */
498
        public UserService getUserService() {
499
                return getService(UserService.class);
1✔
500
        }
501
        
502
        /**
503
         * @param userService the userService to set
504
         */
505
        public void setUserService(UserService userService) {
506
                setService(UserService.class, userService);
1✔
507
        }
1✔
508
        
509
        /**
510
         * Gets the MessageSourceService used in the context.
511
         *
512
         * @return MessageSourceService
513
         */
514
        public MessageSourceService getMessageSourceService() {
515
                try {
516
                        return getService(MessageSourceService.class);
1✔
517
                }
UNCOV
518
                catch (APIException ex) {
×
519
                        //must be a service not found exception because of spring not being started
UNCOV
520
                        return DefaultMessageSourceServiceImpl.getInstance();
×
521
                }
522
        }
523
        
524
        /**
525
         * Sets the MessageSourceService used in the context.
526
         *
527
         * @param messageSourceService the MessageSourceService to use
528
         */
529
        public void setMessageSourceService(MessageSourceService messageSourceService) {
530
                setService(MessageSourceService.class, messageSourceService);
1✔
531
        }
1✔
532
        
533
        /**
534
         * @param cls
535
         * @param advisor
536
         */
537
        public void addAdvisor(Class cls, Advisor advisor) {
538
                Advised advisedService = (Advised) services.get(cls);
×
539
                if (advisedService.indexOf(advisor) < 0) {
×
540
                        advisedService.addAdvisor(advisor);
×
541
                }
542
                addedAdvisors.computeIfAbsent(cls, k -> new HashSet<>());
×
543
                getAddedAdvisors(cls).add(advisor);
×
544
        }
×
545
        
546
        /**
547
         * @param cls
548
         * @param advice
549
         */
550
        public void addAdvice(Class cls, Advice advice) {
551
                Advised advisedService = (Advised) services.get(cls);
×
552
                if (advisedService.indexOf(advice) < 0) {
×
553
                        advisedService.addAdvice(advice);
×
554
                }
555
                addedAdvice.computeIfAbsent(cls, k -> new HashSet<>());
×
556
                getAddedAdvice(cls).add(advice);
×
557
        }
×
558
        
559
        /**
560
         * @param cls
561
         * @param advisor
562
         */
563
        public void removeAdvisor(Class cls, Advisor advisor) {
564
                Advised advisedService = (Advised) services.get(cls);
×
565
                advisedService.removeAdvisor(advisor);
×
566
                getAddedAdvisors(cls).remove(advisor);
×
567
        }
×
568
        
569
        /**
570
         * @param cls
571
         * @param advice
572
         */
573
        public void removeAdvice(Class cls, Advice advice) {
574
                Advised advisedService = (Advised) services.get(cls);
×
575
                advisedService.removeAdvice(advice);
×
576
                getAddedAdvice(cls).remove(advice);
×
577
        }
×
578
        
579
        /**
580
         * Moves advisors and advice added by ServiceContext from the source service to the target one.
581
         *
582
         * @param source the existing service
583
         * @param target the new service
584
         */
585
        private void moveAddedAOP(Advised source, Advised target) {
586
                Class serviceClass = source.getClass();
1✔
587
                Set<Advisor> existingAdvisors = getAddedAdvisors(serviceClass);
1✔
588
                for (Advisor advisor : existingAdvisors) {
1✔
589
                        target.addAdvisor(advisor);
×
590
                        source.removeAdvisor(advisor);
×
591
                }
×
592
                
593
                Set<Advice> existingAdvice = getAddedAdvice(serviceClass);
1✔
594
                for (Advice advice : existingAdvice) {
1✔
595
                        target.addAdvice(advice);
×
596
                        source.removeAdvice(advice);
×
597
                }
×
598
        }
1✔
599
        
600
        /**
601
         * Removes all advice and advisors added by ServiceContext.
602
         *
603
         * @param cls the class of the cached service to cleanup
604
         */
605
        private void removeAddedAOP(Class cls) {
606
                removeAddedAdvisors(cls);
1✔
607
                removeAddedAdvice(cls);
1✔
608
        }
1✔
609
        
610
        /**
611
         * Removes all the advisors added by ServiceContext.
612
         *
613
         * @param cls the class of the cached service to cleanup
614
         */
615
        private void removeAddedAdvisors(Class cls) {
616
                Advised advisedService = (Advised) services.get(cls);
1✔
617
                Set<Advisor> advisorsToRemove = addedAdvisors.get(cls);
1✔
618
                if (advisedService != null && advisorsToRemove != null) {
1✔
619
                        for (Advisor advisor : advisorsToRemove.toArray(new Advisor[] {})) {
×
620
                                removeAdvisor(cls, advisor);
×
621
                        }
622
                }
623
        }
1✔
624
        
625
        /**
626
         * Returns the set of advisors added by ServiceContext.
627
         *
628
         * @param cls the class of the cached service
629
         * @return the set of advisors or an empty set
630
         */
631
        @SuppressWarnings("unchecked")
632
        private Set<Advisor> getAddedAdvisors(Class cls) {
633
                Set<Advisor> result = addedAdvisors.get(cls);
1✔
634
                return (Set<Advisor>) (result == null ? Collections.emptySet() : result);
1✔
635
        }
636
        
637
        /**
638
         * Removes all the advice added by the ServiceContext.
639
         *
640
         * @param cls the class of the caches service to cleanup
641
         */
642
        private void removeAddedAdvice(Class cls) {
643
                Advised advisedService = (Advised) services.get(cls);
1✔
644
                Set<Advice> adviceToRemove = addedAdvice.get(cls);
1✔
645
                if (advisedService != null && adviceToRemove != null) {
1✔
646
                        for (Advice advice : adviceToRemove.toArray(new Advice[] {})) {
×
647
                                removeAdvice(cls, advice);
×
648
                        }
649
                }
650
        }
1✔
651
        
652
        /**
653
         * Returns the set of advice added by ServiceContext.
654
         *
655
         * @param cls the class of the cached service
656
         * @return the set of advice or an empty set
657
         */
658
        @SuppressWarnings("unchecked")
659
        private Set<Advice> getAddedAdvice(Class cls) {
660
                Set<Advice> result = addedAdvice.get(cls);
1✔
661
                return (Set<Advice>) (result == null ? Collections.emptySet() : result);
1✔
662
        }
663
        
664
        /**
665
         * Returns the current proxy that is stored for the Class <code>cls</code>
666
         *
667
         * @param cls
668
         * @return Object that is a proxy for the <code>cls</code> class
669
         */
670
        @SuppressWarnings("unchecked")
671
        public <T> T getService(Class<? extends T> cls) {
672
                if (log.isTraceEnabled()) {
1✔
673
                        log.trace("Getting service: " + cls);
×
674
                }
675
                
676
                // if the context is refreshing, wait until it is
677
                // done -- otherwise a null service might be returned
678
                synchronized (refreshingContextLock) {
1✔
679
                        try {
680
                                while (refreshingContext) {
1✔
681
                                        log.debug("Waiting to get service: {} while the context is being refreshed", cls);
×
682
                                        
683
                                        refreshingContextLock.wait();
×
684
                                        
685
                                        log.debug("Finished waiting to get service {} while the context was being refreshed", cls);
×
686
                                }
687
                                
688
                        }
689
                        catch (InterruptedException e) {
×
690
                                log.warn("Refresh lock was interrupted", e);
×
691
                        }
1✔
692
                }
1✔
693
                
694
                Object service = services.get(cls);
1✔
695
                if (service == null) {
1✔
696
                        throw new ServiceNotFoundException(cls);
1✔
697
                }
698
                
699
                return (T) service;
1✔
700
        }
701
        
702
        /**
703
         * Allow other services to be added to our service layer
704
         *
705
         * @param cls Interface to proxy
706
         * @param classInstance the actual instance of the <code>cls</code> interface
707
         */
708
        public void setService(Class<?> cls, Object classInstance) {
709
                log.debug("Setting service: {}", cls);
1✔
710
                
711
                if (cls != null && classInstance != null) {
1✔
712
                        try {
713
                                Advised cachedService = (Advised) services.get(cls);
1✔
714
                                boolean noExistingService = cachedService == null;
1✔
715
                                boolean replacingService = cachedService != null && cachedService != classInstance;
1✔
716
                                boolean serviceAdvised = classInstance instanceof Advised;
1✔
717
                                
718
                                if (noExistingService || replacingService) {
1✔
719
                                        
720
                                        Advised advisedService;
721
                                        
722
                                        if (!serviceAdvised) {
1✔
723
                                                // Adding a bare service, wrap with AOP proxy
724
                                                Class[] interfaces = { cls };
1✔
725
                                                ProxyFactory factory = new ProxyFactory(interfaces);
1✔
726
                                                factory.setTarget(classInstance);
1✔
727
                                                advisedService = (Advised) factory.getProxy(OpenmrsClassLoader.getInstance());
1✔
728
                                        } else {
1✔
729
                                                advisedService = (Advised) classInstance;
1✔
730
                                        }
731
                                        
732
                                        if (replacingService) {
1✔
733
                                                moveAddedAOP(cachedService, advisedService);
1✔
734
                                        }
735
                                        
736
                                        services.put(cls, advisedService);
1✔
737
                                }
738
                                log.debug("Service: {} set successfully", cls);
1✔
739
                        }
740
                        catch (Exception e) {
×
741
                                throw new APIException("service.unable.create.proxy.factory", new Object[] { classInstance.getClass()
×
742
                                        .getName() }, e);
×
743
                        }
1✔
744
                        
745
                }
746
        }
1✔
747
        
748
        /**
749
         * Allow other services to be added to our service layer <br>
750
         * <br>
751
         * Classes will be found/loaded with the ModuleClassLoader <br>
752
         * <br>
753
         * <code>params</code>[0] = string representing the service interface<br>
754
         * <code>params</code>[1] = service instance
755
         *
756
         * @param params list of parameters
757
         */
758
        public void setModuleService(List<Object> params) {
759
                String classString = (String) params.get(0);
1✔
760
                Object classInstance = params.get(1);
1✔
761
                
762
                if (classString == null || classInstance == null) {
1✔
763
                        throw new APIException(
1✔
764
                                String.format("Unable to find service as unexpected null value found for class [%s] or instance [%s]",
1✔
765
                                    classString, classInstance));
766
                }
767
                
768
                Class cls = null;
1✔
769
                
770
                // load the given 'classString' class from either the openmrs class
771
                // loader or the system class loader depending on if we're in a testing
772
                // environment or not (system == testing, openmrs == normal)
773
                try {
774
                        if (!useSystemClassLoader) {
1✔
775
                                cls = OpenmrsClassLoader.getInstance().loadClass(classString);
1✔
776
                                
777
                                if (cls != null && log.isDebugEnabled()) {
1✔
778
                                        try {
779
                                                log.debug("cls classloader: {} uid: {}", cls.getClass().getClassLoader(),
×
780
                                                    cls.getClass().getClassLoader().hashCode());
×
781
                                        }
782
                                        catch (Exception e) { /*pass*/}
×
783
                                }
784
                        } else if (useSystemClassLoader) {
×
785
                                try {
786
                                        cls = Class.forName(classString);
×
787
                                        log.debug("cls2 classloader: {} uid: {}", cls.getClass().getClassLoader(),
×
788
                                            cls.getClass().getClassLoader().hashCode());
×
789
                                        //pay attention that here, cls = Class.forName(classString), the system class loader and
790
                                        //cls2 is the openmrs class loader, like above.
791
                                        log.debug("cls==cls2: {}",
×
792
                                            String.valueOf(cls == OpenmrsClassLoader.getInstance().loadClass(classString)));
×
793
                                }
794
                                catch (Exception e) { /*pass*/}
×
795
                        }
796
                }
797
                catch (ClassNotFoundException e) {
1✔
798
                        throw new APIException("Unable to find service as class not found: " + classString, e);
1✔
799
                }
1✔
800
                
801
                // add this module service to the normal list of services
802
                setService(cls, classInstance);
1✔
803
                
804
                //Run onStartup for all services implementing the OpenmrsService interface.
805
                if (OpenmrsService.class.isAssignableFrom(classInstance.getClass())) {
1✔
806
                        moduleOpenmrsServices.put(classString, (OpenmrsService) classInstance);
1✔
807
                        runOpenmrsServiceOnStartup((OpenmrsService) classInstance, classString);
1✔
808
                }
809
        }
1✔
810
        
811
        /**
812
         * Set this service context to use the system class loader if the
813
         * <code>useSystemClassLoader</code> is set to true. If false, the openmrs class loader is used
814
         * to load module services
815
         *
816
         * @param useSystemClassLoader true/false whether to use the system class loader
817
         */
818
        public void setUseSystemClassLoader(boolean useSystemClassLoader) {
819
                this.useSystemClassLoader = useSystemClassLoader;
1✔
820
        }
1✔
821
        
822
        /**
823
         * Checks if we are using the system class loader.
824
         *
825
         * @return true if using the system class loader, else false.
826
         */
827
        public boolean isUseSystemClassLoader() {
828
                return useSystemClassLoader;
1✔
829
        }
830
        
831
        public static void setRefreshingContext(boolean refreshingContext) {
832
                ServiceContext.refreshingContext = refreshingContext;
×
833
        }
×
834
        
835
        /**
836
         * Should be called <b>right before</b> any spring context refresh This forces all calls to
837
         * getService to wait until <code>doneRefreshingContext</code> is called
838
         */
839
        public void startRefreshingContext() {
840
                synchronized (refreshingContextLock) {
×
841
                        log.info("Refreshing Context");
×
842
                        setRefreshingContext(true);
×
843
                }
×
844
        }
×
845
        
846
        /**
847
         * Should be called <b>right after</b> any spring context refresh This wakes up all calls to
848
         * getService that were waiting because <code>startRefreshingContext</code> was called
849
         */
850
        public void doneRefreshingContext() {
851
                synchronized (refreshingContextLock) {
×
852
                        log.info("Done refreshing Context");
×
853
                        setRefreshingContext(false);
×
854
                        refreshingContextLock.notifyAll();
×
855
                }
×
856
        }
×
857
        
858
        /**
859
         * Returns true/false whether startRefreshingContext() has been called without a subsequent call
860
         * to doneRefreshingContext() yet. All methods involved in starting/stopping a module should
861
         * call this if a service method is needed -- otherwise a deadlock will occur.
862
         *
863
         * @return true/false whether the services are currently blocking waiting for a call to
864
         *         doneRefreshingContext()
865
         */
866
        public boolean isRefreshingContext() {
867
                synchronized (refreshingContextLock) {
1✔
868
                        return refreshingContext;
1✔
869
                }
870
        }
871
        
872
        /**
873
         * Retrieves all Beans which have been registered in the Spring {@link ApplicationContext} that
874
         * match the given object type (including subclasses).
875
         * <p>
876
         * <b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i> check nested
877
         * beans which might match the specified type as well.
878
         *
879
         * @see ApplicationContext#getBeansOfType(Class)
880
         * @param type the type of Bean to retrieve from the Spring {@link ApplicationContext}
881
         * @return a List of all registered Beans that are valid instances of the passed type
882
         * @since 1.5
883
         * <strong>Should</strong> return a list of all registered beans of the passed type
884
         * <strong>Should</strong> return beans registered in a module
885
         * <strong>Should</strong> return an empty list if no beans have been registered of the passed type
886
         */
887
        
888
        public <T> List<T> getRegisteredComponents(Class<T> type) {
889
                Map<String, T> m = getRegisteredComponents(applicationContext, type);
1✔
890
                log.trace("getRegisteredComponents({}) = {}", type, m);
1✔
891
                return new ArrayList<>(m.values());
1✔
892
        }
893
        
894
        /**
895
         * Retrieves a bean that match the given type (including subclasses) and name.
896
         *
897
         * @param beanName the name of registered bean to retrieve
898
         * @param type the type of bean to retrieve 
899
         * @return bean of passed type
900
         *
901
         * @since 1.9.4
902
         */
903
        public <T> T getRegisteredComponent(String beanName, Class<T> type) throws APIException {
904
                try {
905
                        return applicationContext.getBean(beanName, type);
1✔
906
                }
907
                catch (BeansException beanException) {
1✔
908
                        throw new APIException("Error during getting registered component", beanException);
1✔
909
                }
910
        }
911
        
912
        /**
913
         * Private method which returns all components registered in a Spring applicationContext of a
914
         * given type This method recurses through each parent ApplicationContext
915
         *
916
         * @param context - The applicationContext to check
917
         * @param type - The type of component to retrieve
918
         * @return all components registered in a Spring applicationContext of a given type
919
         */
920
        private <T> Map<String, T> getRegisteredComponents(ApplicationContext context, Class<T> type) {
921
                Map<String, T> registeredComponents = context.getBeansOfType(type);
1✔
922
                log.trace("getRegisteredComponents({}, {}) = {}", context, type, registeredComponents);
1✔
923
                Map<String, T> components = new HashMap<>(registeredComponents);
1✔
924
                if (context.getParent() != null) {
1✔
925
                        components.putAll(getRegisteredComponents(context.getParent(), type));
1✔
926
                }
927
                return components;
1✔
928
        }
929
        
930
        /**
931
         * @param applicationContext the applicationContext to set
932
         */
933
        @Override
934
        public void setApplicationContext(ApplicationContext applicationContext) {
935
                this.applicationContext = applicationContext;
1✔
936
        }
1✔
937
        
938
        public ApplicationContext getApplicationContext() {
939
                return applicationContext;
1✔
940
        }
941
        
942
        /**
943
         * Calls the {@link OpenmrsService#onStartup()} method for an instance implementing the
944
         * {@link OpenmrsService} interface.
945
         *
946
         * @param openmrsService instance implementing the {@link OpenmrsService} interface.
947
         * @param classString the full instance class name including the package name.
948
         * @since 1.9
949
         */
950
        private void runOpenmrsServiceOnStartup(final OpenmrsService openmrsService, final String classString) {
951
                OpenmrsThreadPoolHolder.threadExecutor.execute(() -> {
1✔
952
                        try {
953
                                synchronized (refreshingContextLock) {
1✔
954
                                        //Need to wait for application context to finish refreshing otherwise we get into trouble.
955
                                        while (refreshingContext) {
1✔
956
                                                log.debug("Waiting to get service: {} while the context is being refreshed", classString);
×
957
        
958
                                                refreshingContextLock.wait();
×
959
        
960
                                                log.debug("Finished waiting to get service {} while the context was being refreshed", classString);
×
961
                                        }
962
                                }
1✔
963
        
964
                                Daemon.runStartupForService(openmrsService);
1✔
965
                        }
966
                                catch (InterruptedException e) {
×
967
                                log.warn("Refresh lock was interrupted while waiting to run OpenmrsService.onStartup() for "
×
968
                                        + classString, e);
969
                        }
1✔
970
                });
1✔
971
        }
1✔
972
        
973
        /**
974
         * Gets a list of services implementing the {@link OpenmrsService} interface, for a given
975
         * module.
976
         *
977
         * @param modulePackage the module's package name.
978
         * @return the list of service instances.
979
         * @since 1.9
980
         */
981
        public List<OpenmrsService> getModuleOpenmrsServices(String modulePackage) {
982
                List<OpenmrsService> openmrsServices = new ArrayList<>();
1✔
983
                
984
                for (Entry<String, OpenmrsService> entry : moduleOpenmrsServices.entrySet()) {
1✔
985
                        if (entry.getKey().startsWith(modulePackage)) {
1✔
986
                                openmrsServices.add(entry.getValue());
1✔
987
                        }
988
                }
1✔
989
                
990
                return openmrsServices;
1✔
991
        }
992
        
993
        /**
994
         * Gets the visit service.
995
         *
996
         * @return visit service.
997
         * @since 1.9
998
         **/
999
        public VisitService getVisitService() {
1000
                return getService(VisitService.class);
1✔
1001
        }
1002
        
1003
        /**
1004
         * Sets the visit service.
1005
         *
1006
         * @param visitService the visitService to set
1007
         * @since 1.9
1008
         **/
1009
        public void setVisitService(VisitService visitService) {
1010
                setService(VisitService.class, visitService);
1✔
1011
        }
1✔
1012
        
1013
        /**
1014
         * Gets the provider service.
1015
         *
1016
         * @return provider service.
1017
         * @since 1.9
1018
         **/
1019
        
1020
        public ProviderService getProviderService() {
1021
                return getService(ProviderService.class);
1✔
1022
        }
1023
        
1024
        /**
1025
         * Sets the provider service.
1026
         *
1027
         * @param providerService the providerService to set
1028
         * @since 1.9
1029
         **/
1030
        public void setProviderService(ProviderService providerService) {
1031
                setService(ProviderService.class, providerService);
1✔
1032
        }
1✔
1033
        
1034
        /**
1035
         * Gets the datatype service
1036
         *
1037
         * @return custom datatype service
1038
         * @since 1.9
1039
         */
1040
        public DatatypeService getDatatypeService() {
1041
                return getService(DatatypeService.class);
1✔
1042
        }
1043
        
1044
        /**
1045
         * Sets the datatype service
1046
         *
1047
         * @param datatypeService the datatypeService to set
1048
         * @since 1.9
1049
         */
1050
        public void setDatatypeService(DatatypeService datatypeService) {
1051
                setService(DatatypeService.class, datatypeService);
1✔
1052
        }
1✔
1053

1054
        /**
1055
         * Clears entire API cache.
1056
         * 
1057
         * @since 2.8.0
1058
         */
1059
        public void clearEntireApiCache() {
1060
                CacheManager apiCacheManager = getRegisteredComponent("apiCacheManager", CacheManager.class);
1✔
1061
                apiCacheManager.getCacheNames().forEach(cacheName -> apiCacheManager.getCache(cacheName).invalidate());
1✔
1062
        }
1✔
1063
}
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