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

openmrs / openmrs-core / 30404913075

28 Jul 2026 10:32PM UTC coverage: 64.323% (-0.1%) from 64.425%
30404913075

push

github

ibacher
Keep allowerasing only in the development stage

24615 of 38268 relevant lines covered (64.32%)

0.64 hits per line

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

68.38
/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
import java.util.concurrent.ConcurrentHashMap;
21

22
import org.aopalliance.aop.Advice;
23
import org.openmrs.api.APIException;
24
import org.openmrs.api.AdministrationService;
25
import org.openmrs.api.CohortService;
26
import org.openmrs.api.ConceptService;
27
import org.openmrs.api.ConditionService;
28
import org.openmrs.api.DatatypeService;
29
import org.openmrs.api.DiagnosisService;
30
import org.openmrs.api.EncounterService;
31
import org.openmrs.api.FormService;
32
import org.openmrs.api.LocationService;
33
import org.openmrs.api.MedicationDispenseService;
34
import org.openmrs.api.ObsService;
35
import org.openmrs.api.OpenmrsService;
36
import org.openmrs.api.OrderService;
37
import org.openmrs.api.OrderSetService;
38
import org.openmrs.api.PatientService;
39
import org.openmrs.api.PersonService;
40
import org.openmrs.api.ProgramWorkflowService;
41
import org.openmrs.api.ProviderService;
42
import org.openmrs.api.SerializationService;
43
import org.openmrs.api.ServiceNotFoundException;
44
import org.openmrs.api.UserService;
45
import org.openmrs.api.VisitService;
46
import org.openmrs.hl7.HL7Service;
47
import org.openmrs.logic.LogicService;
48
import org.openmrs.messagesource.MessageSourceService;
49
import org.openmrs.messagesource.impl.DefaultMessageSourceServiceImpl;
50
import org.openmrs.notification.AlertService;
51
import org.openmrs.notification.MessageService;
52
import org.openmrs.scheduler.SchedulerService;
53
import org.openmrs.util.OpenmrsClassLoader;
54
import org.openmrs.util.OpenmrsThreadPoolHolder;
55
import org.slf4j.Logger;
56
import org.slf4j.LoggerFactory;
57
import org.springframework.aop.Advisor;
58
import org.springframework.aop.framework.Advised;
59
import org.springframework.aop.framework.ProxyFactory;
60
import org.springframework.beans.BeansException;
61
import org.springframework.cache.CacheManager;
62
import org.springframework.context.ApplicationContext;
63
import org.springframework.context.ApplicationContextAware;
64

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

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

123
                private ServiceContextHolder() {
124
                }
125

126
                private static ServiceContext instance = null;
1✔
127
        }
128
        
129
        /**
130
         * There should only be one ServiceContext per openmrs (java virtual machine). This method
131
         * should be used when wanting to fetch the service context Note: The ServiceContext shouldn't
132
         * be used independently. All calls should go through the Context
133
         *
134
         * @return This VM's current ServiceContext.
135
         * @see org.openmrs.api.context.Context
136
         */
137
        public static ServiceContext getInstance() {
138
                if (ServiceContextHolder.instance == null) {
1✔
139
                        ServiceContextHolder.instance = new ServiceContext();
1✔
140
                }
141
                
142
                return ServiceContextHolder.instance;
1✔
143
        }
144
        
145
        /**
146
         * Reports whether the singleton {@link ServiceContext} has already been created, without triggering
147
         * its creation the way {@link #getInstance()} would.
148
         *
149
         * @return true if the ServiceContext singleton has been instantiated
150
         * @since 3.0.0, 2.9.0, 2.8.9
151
         */
152
        public static boolean isInstantiated() {
153
                return ServiceContextHolder.instance != null;
1✔
154
        }
155

156
        /**
157
         * Null out the current instance of the ServiceContext. This should be used when modules are
158
         * refreshing (being added/removed) and/or openmrs is shutting down
159
         */
160
        public static void destroyInstance() {
161
                if (ServiceContextHolder.instance != null && ServiceContextHolder.instance.services != null) {
×
162
                        for (Map.Entry<Class, Object> entry : ServiceContextHolder.instance.services.entrySet()) {
×
163
                                log.debug("Service - {} : {}", entry.getKey().getName(), entry.getValue());
×
164
                        }
×
165
                        
166
                        // Remove advice and advisors that this service added
167
                        for (Class serviceClass : ServiceContextHolder.instance.services.keySet()) {
×
168
                                ServiceContextHolder.instance.removeAddedAOP(serviceClass);
×
169
                        }
×
170
                        
171
                        if (ServiceContextHolder.instance.services != null) {
×
172
                                ServiceContextHolder.instance.services.clear();
×
173
                                ServiceContextHolder.instance.services = null;
×
174
                        }
175
                        
176
                        if (ServiceContextHolder.instance.addedAdvisors != null) {
×
177
                                ServiceContextHolder.instance.addedAdvisors.clear();
×
178
                                ServiceContextHolder.instance.addedAdvisors = null;
×
179
                        }
180
                        
181
                        if (ServiceContextHolder.instance.addedAdvice != null) {
×
182
                                ServiceContextHolder.instance.addedAdvice.clear();
×
183
                                ServiceContextHolder.instance.addedAdvice = null;
×
184
                        }
185
                }
186
                
187
                if (ServiceContextHolder.instance != null) {
×
188
                        ServiceContextHolder.instance.applicationContext = null;
×
189
                        
190
                        if (ServiceContextHolder.instance.moduleOpenmrsServices != null) {
×
191
                                ServiceContextHolder.instance.moduleOpenmrsServices.clear();
×
192
                                ServiceContextHolder.instance.moduleOpenmrsServices = null;
×
193
                        }
194
                }
195
                log.debug("Destroying ServiceContext instance: {}", ServiceContextHolder.instance);
×
196
                ServiceContextHolder.instance = null;
×
197
        }
×
198
        
199
        /**
200
         * @return encounter-related services
201
         */
202
        public EncounterService getEncounterService() {
203
                return getService(EncounterService.class);
1✔
204
        }
205
        
206
        /**
207
         * @return location services
208
         */
209
        public LocationService getLocationService() {
210
                return getService(LocationService.class);
1✔
211
        }
212
        
213
        /**
214
         * @return observation services
215
         */
216
        public ObsService getObsService() {
217
                return getService(ObsService.class);
1✔
218
        }
219
        
220
        /**
221
         * @return condition related service
222
         * 
223
         * @since 2.2
224
         */
225
        public ConditionService getConditionService() {
226
                return getService(ConditionService.class);
1✔
227
        }
228
        
229
        /**
230
         * @param conditionService condition related service
231
         *            
232
         * @since 2.2   
233
         */
234
        public void setConditionService(ConditionService conditionService) {
235
                setService(ConditionService.class, conditionService);
1✔
236
        }
1✔
237

238
        /**
239
         * @return diagnosis related service
240
         *
241
         * @since 2.2
242
         */
243
        public DiagnosisService getDiagnosisService() {
244
                return getService(DiagnosisService.class);
1✔
245
        }
246

247
        /**
248
         * @param diagnosisService diagnosis related service
249
         *
250
         * @since 2.2
251
         */
252
        public void setDiagnosisService(DiagnosisService diagnosisService) {
253
                setService(DiagnosisService.class, diagnosisService);
1✔
254
        }
1✔
255

256
        /**
257
         * @return MedicationDispense related service
258
         * @since 2.6.0
259
         */
260
        public MedicationDispenseService getMedicationDispenseService() {
261
                return getService(MedicationDispenseService.class);
1✔
262
        }
263

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

328
        /**
329
         * @return logicService
330
         */
331
        public LogicService getLogicService() {
332
                return getService(LogicService.class);
×
333
        }
334
        
335
        /**
336
         * @return scheduler service
337
         */
338
        public SchedulerService getSchedulerService() {
339
                return getService(SchedulerService.class);
×
340
        }
341
        
342
        /**
343
         * Set the scheduler service.
344
         *
345
         * @param schedulerService
346
         */
347
        public void setSchedulerService(SchedulerService schedulerService) {
348
                setService(SchedulerService.class, schedulerService);
1✔
349
        }
1✔
350
        
351
        /**
352
         * @return alert service
353
         */
354
        public AlertService getAlertService() {
355
                return getService(AlertService.class);
1✔
356
        }
357
        
358
        /**
359
         * @param alertService
360
         */
361
        public void setAlertService(AlertService alertService) {
362
                setService(AlertService.class, alertService);
1✔
363
        }
1✔
364
        
365
        /**
366
         * @param programWorkflowService
367
         */
368
        public void setProgramWorkflowService(ProgramWorkflowService programWorkflowService) {
369
                setService(ProgramWorkflowService.class, programWorkflowService);
1✔
370
        }
1✔
371

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

444
        /**
445
         * @param orderService the orderService to set
446
         */
447
        public void setOrderService(OrderService orderService) {
448
                setService(OrderService.class, orderService);
1✔
449
        }
1✔
450
        
451
        /**
452
         * @param orderSetService the orderSetService to set
453
         */
454
        public void setOrderSetService(OrderSetService orderSetService) {
455
                setService(OrderSetService.class, orderSetService);
1✔
456
        }
1✔
457
        
458
        /**
459
         * @param serializationService
460
         */
461
        public void setSerializationService(SerializationService serializationService) {
462
                setService(SerializationService.class, serializationService);
1✔
463
        }
1✔
464
        
465
        /**
466
         * @return patient related services
467
         */
468
        public PatientService getPatientService() {
469
                return getService(PatientService.class);
1✔
470
        }
471
        
472
        /**
473
         * @param patientService the patientService to set
474
         */
475
        public void setPatientService(PatientService patientService) {
476
                setService(PatientService.class, patientService);
1✔
477
        }
1✔
478
        
479
        /**
480
         * @return person related services
481
         */
482
        public PersonService getPersonService() {
483
                return getService(PersonService.class);
1✔
484
        }
485
        
486
        /**
487
         * @param personService the personService to set
488
         */
489
        public void setPersonService(PersonService personService) {
490
                setService(PersonService.class, personService);
1✔
491
        }
1✔
492
        
493
        /**
494
         * @return concept related services
495
         */
496
        public ConceptService getConceptService() {
497
                return getService(ConceptService.class);
1✔
498
        }
499
        
500
        /**
501
         * @param conceptService the conceptService to set
502
         */
503
        public void setConceptService(ConceptService conceptService) {
504
                setService(ConceptService.class, conceptService);
1✔
505
        }
1✔
506
        
507
        /**
508
         * @return user-related services
509
         */
510
        public UserService getUserService() {
511
                return getService(UserService.class);
1✔
512
        }
513
        
514
        /**
515
         * @param userService the userService to set
516
         */
517
        public void setUserService(UserService userService) {
518
                setService(UserService.class, userService);
1✔
519
        }
1✔
520
        
521
        /**
522
         * Gets the MessageSourceService used in the context.
523
         *
524
         * @return MessageSourceService
525
         */
526
        public MessageSourceService getMessageSourceService() {
527
                try {
528
                        return getService(MessageSourceService.class);
1✔
529
                }
530
                catch (APIException ex) {
×
531
                        //must be a service not found exception because of spring not being started
532
                        return DefaultMessageSourceServiceImpl.getInstance();
×
533
                }
534
        }
535
        
536
        /**
537
         * Sets the MessageSourceService used in the context.
538
         *
539
         * @param messageSourceService the MessageSourceService to use
540
         */
541
        public void setMessageSourceService(MessageSourceService messageSourceService) {
542
                setService(MessageSourceService.class, messageSourceService);
1✔
543
        }
1✔
544
        
545
        /**
546
         * @param cls
547
         * @param advisor
548
         */
549
        public void addAdvisor(Class cls, Advisor advisor) {
550
                Advised advisedService = (Advised) services.get(cls);
×
551
                if (advisedService.indexOf(advisor) < 0) {
×
552
                        advisedService.addAdvisor(advisor);
×
553
                }
554
                addedAdvisors.computeIfAbsent(cls, k -> new HashSet<>());
×
555
                getAddedAdvisors(cls).add(advisor);
×
556
        }
×
557
        
558
        /**
559
         * @param cls
560
         * @param advice
561
         */
562
        public void addAdvice(Class cls, Advice advice) {
563
                Advised advisedService = (Advised) services.get(cls);
×
564
                if (advisedService.indexOf(advice) < 0) {
×
565
                        advisedService.addAdvice(advice);
×
566
                }
567
                addedAdvice.computeIfAbsent(cls, k -> new HashSet<>());
×
568
                getAddedAdvice(cls).add(advice);
×
569
        }
×
570
        
571
        /**
572
         * @param cls
573
         * @param advisor
574
         */
575
        public void removeAdvisor(Class cls, Advisor advisor) {
576
                Advised advisedService = (Advised) services.get(cls);
×
577
                advisedService.removeAdvisor(advisor);
×
578
                getAddedAdvisors(cls).remove(advisor);
×
579
        }
×
580
        
581
        /**
582
         * @param cls
583
         * @param advice
584
         */
585
        public void removeAdvice(Class cls, Advice advice) {
586
                Advised advisedService = (Advised) services.get(cls);
×
587
                advisedService.removeAdvice(advice);
×
588
                getAddedAdvice(cls).remove(advice);
×
589
        }
×
590
        
591
        /**
592
         * Moves advisors and advice added by ServiceContext from the source service to the target one.
593
         *
594
         * @param source the existing service
595
         * @param target the new service
596
         */
597
        private void moveAddedAOP(Advised source, Advised target) {
598
                Class serviceClass = source.getClass();
1✔
599
                Set<Advisor> existingAdvisors = getAddedAdvisors(serviceClass);
1✔
600
                for (Advisor advisor : existingAdvisors) {
1✔
601
                        target.addAdvisor(advisor);
×
602
                        source.removeAdvisor(advisor);
×
603
                }
×
604
                
605
                Set<Advice> existingAdvice = getAddedAdvice(serviceClass);
1✔
606
                for (Advice advice : existingAdvice) {
1✔
607
                        target.addAdvice(advice);
×
608
                        source.removeAdvice(advice);
×
609
                }
×
610
        }
1✔
611
        
612
        /**
613
         * Removes all advice and advisors added by ServiceContext.
614
         *
615
         * @param cls the class of the cached service to cleanup
616
         */
617
        private void removeAddedAOP(Class cls) {
618
                removeAddedAdvisors(cls);
×
619
                removeAddedAdvice(cls);
×
620
        }
×
621
        
622
        /**
623
         * Removes all the advisors added by ServiceContext.
624
         *
625
         * @param cls the class of the cached service to cleanup
626
         */
627
        private void removeAddedAdvisors(Class cls) {
628
                Advised advisedService = (Advised) services.get(cls);
×
629
                Set<Advisor> advisorsToRemove = addedAdvisors.get(cls);
×
630
                if (advisedService != null && advisorsToRemove != null) {
×
631
                        for (Advisor advisor : advisorsToRemove.toArray(new Advisor[] {})) {
×
632
                                removeAdvisor(cls, advisor);
×
633
                        }
634
                }
635
        }
×
636
        
637
        /**
638
         * Returns the set of advisors added by ServiceContext.
639
         *
640
         * @param cls the class of the cached service
641
         * @return the set of advisors or an empty set
642
         */
643
        @SuppressWarnings("unchecked")
644
        private Set<Advisor> getAddedAdvisors(Class cls) {
645
                Set<Advisor> result = addedAdvisors.get(cls);
1✔
646
                return (Set<Advisor>) (result == null ? Collections.emptySet() : result);
1✔
647
        }
648
        
649
        /**
650
         * Removes all the advice added by the ServiceContext.
651
         *
652
         * @param cls the class of the caches service to cleanup
653
         */
654
        private void removeAddedAdvice(Class cls) {
655
                Advised advisedService = (Advised) services.get(cls);
×
656
                Set<Advice> adviceToRemove = addedAdvice.get(cls);
×
657
                if (advisedService != null && adviceToRemove != null) {
×
658
                        for (Advice advice : adviceToRemove.toArray(new Advice[] {})) {
×
659
                                removeAdvice(cls, advice);
×
660
                        }
661
                }
662
        }
×
663
        
664
        /**
665
         * Returns the set of advice added by ServiceContext.
666
         *
667
         * @param cls the class of the cached service
668
         * @return the set of advice or an empty set
669
         */
670
        @SuppressWarnings("unchecked")
671
        private Set<Advice> getAddedAdvice(Class cls) {
672
                Set<Advice> result = addedAdvice.get(cls);
1✔
673
                return (Set<Advice>) (result == null ? Collections.emptySet() : result);
1✔
674
        }
675
        
676
        /**
677
         * Returns the current proxy that is stored for the Class <code>cls</code>
678
         *
679
         * @param cls
680
         * @return Object that is a proxy for the <code>cls</code> class
681
         */
682
        @SuppressWarnings("unchecked")
683
        public <T> T getService(Class<? extends T> cls) {
684
                if (log.isTraceEnabled()) {
1✔
685
                        log.trace("Getting service: " + cls);
×
686
                }
687
                
688
                // Only synchronize if we're refreshing the context; during context refreshes, we need
689
                // to wait for services to be available. Otherwise we can take the fast-path.
690
                if (refreshingContext) {
1✔
691
                        synchronized (refreshingContextLock) {
1✔
692
                                try {
693
                                        while (refreshingContext) {
1✔
694
                                                log.debug("Waiting to get service: {} while the context is being refreshed", cls);
1✔
695

696
                                                refreshingContextLock.wait();
1✔
697

698
                                                log.debug("Finished waiting to get service {} while the context was being refreshed", cls);
1✔
699
                                        }
700

701
                                } catch (InterruptedException e) {
×
702
                                        log.warn("Refresh lock was interrupted", e);
×
703
                                }
1✔
704
                        }
1✔
705
                }
706
                
707
                Object service = services.get(cls);
1✔
708
                if (service == null) {
1✔
709
                        throw new ServiceNotFoundException(cls);
1✔
710
                }
711
                
712
                return (T) service;
1✔
713
        }
714
        
715
        /**
716
         * Allow other services to be added to our service layer
717
         *
718
         * @param cls Interface to proxy
719
         * @param classInstance the actual instance of the <code>cls</code> interface
720
         */
721
        public void setService(Class<?> cls, Object classInstance) {
722
                log.debug("Setting service: {}", cls);
1✔
723
                
724
                if (cls != null && classInstance != null) {
1✔
725
                        try {
726
                                Advised cachedService = (Advised) services.get(cls);
1✔
727
                                boolean noExistingService = cachedService == null;
1✔
728
                                boolean replacingService = cachedService != null && cachedService != classInstance;
1✔
729
                                boolean serviceAdvised = classInstance instanceof Advised;
1✔
730
                                
731
                                if (noExistingService || replacingService) {
1✔
732
                                        
733
                                        Advised advisedService;
734
                                        
735
                                        if (!serviceAdvised) {
1✔
736
                                                // Adding a bare service, wrap with AOP proxy
737
                                                Class[] interfaces = { cls };
1✔
738
                                                ProxyFactory factory = new ProxyFactory(interfaces);
1✔
739
                                                factory.setTarget(classInstance);
1✔
740
                                                advisedService = (Advised) factory.getProxy(OpenmrsClassLoader.getInstance());
1✔
741
                                        } else {
1✔
742
                                                advisedService = (Advised) classInstance;
1✔
743
                                        }
744
                                        
745
                                        if (replacingService) {
1✔
746
                                                moveAddedAOP(cachedService, advisedService);
1✔
747
                                        }
748
                                        
749
                                        services.put(cls, advisedService);
1✔
750
                                }
751
                                log.debug("Service: {} set successfully", cls);
1✔
752
                        }
753
                        catch (Exception e) {
×
754
                                throw new APIException("service.unable.create.proxy.factory", new Object[] { classInstance.getClass()
×
755
                                        .getName() }, e);
×
756
                        }
1✔
757
                        
758
                }
759
        }
1✔
760
        
761
        /**
762
         * Allow other services to be added to our service layer <br>
763
         * <br>
764
         * Classes will be found/loaded with the ModuleClassLoader <br>
765
         * <br>
766
         * <code>params</code>[0] = string representing the service interface<br>
767
         * <code>params</code>[1] = service instance
768
         *
769
         * @param params list of parameters
770
         */
771
        public void setModuleService(List<Object> params) {
772
                String classString = (String) params.get(0);
1✔
773
                Object classInstance = params.get(1);
1✔
774
                
775
                if (classString == null || classInstance == null) {
1✔
776
                        throw new APIException(
1✔
777
                                String.format("Unable to find service as unexpected null value found for class [%s] or instance [%s]",
1✔
778
                                    classString, classInstance));
779
                }
780
                
781
                Class cls = null;
1✔
782
                
783
                // load the given 'classString' class from either the openmrs class
784
                // loader or the system class loader depending on if we're in a testing
785
                // environment or not (system == testing, openmrs == normal)
786
                try {
787
                        if (!useSystemClassLoader) {
1✔
788
                                cls = OpenmrsClassLoader.getInstance().loadClass(classString);
1✔
789
                                
790
                                if (cls != null && log.isDebugEnabled()) {
1✔
791
                                        try {
792
                                                log.debug("cls classloader: {} uid: {}", cls.getClass().getClassLoader(),
×
793
                                                    cls.getClass().getClassLoader().hashCode());
×
794
                                        }
795
                                        catch (Exception e) { /*pass*/}
×
796
                                }
797
                        } else if (useSystemClassLoader) {
×
798
                                try {
799
                                        cls = Class.forName(classString);
×
800
                                        log.debug("cls2 classloader: {} uid: {}", cls.getClass().getClassLoader(),
×
801
                                            cls.getClass().getClassLoader().hashCode());
×
802
                                        //pay attention that here, cls = Class.forName(classString), the system class loader and
803
                                        //cls2 is the openmrs class loader, like above.
804
                                        log.debug("cls==cls2: {}",
×
805
                                            String.valueOf(cls == OpenmrsClassLoader.getInstance().loadClass(classString)));
×
806
                                }
807
                                catch (Exception e) { /*pass*/}
×
808
                        }
809
                }
810
                catch (ClassNotFoundException e) {
1✔
811
                        throw new APIException("Unable to find service as class not found: " + classString, e);
1✔
812
                }
1✔
813
                
814
                // add this module service to the normal list of services
815
                setService(cls, classInstance);
1✔
816
                
817
                //Run onStartup for all services implementing the OpenmrsService interface.
818
                if (OpenmrsService.class.isAssignableFrom(classInstance.getClass())) {
1✔
819
                        moduleOpenmrsServices.put(classString, (OpenmrsService) classInstance);
1✔
820
                        runOpenmrsServiceOnStartup((OpenmrsService) classInstance, classString);
1✔
821
                }
822
        }
1✔
823
        
824
        /**
825
         * Set this service context to use the system class loader if the
826
         * <code>useSystemClassLoader</code> is set to true. If false, the openmrs class loader is used
827
         * to load module services
828
         *
829
         * @param useSystemClassLoader true/false whether to use the system class loader
830
         */
831
        public void setUseSystemClassLoader(boolean useSystemClassLoader) {
832
                this.useSystemClassLoader = useSystemClassLoader;
1✔
833
        }
1✔
834
        
835
        /**
836
         * Checks if we are using the system class loader.
837
         *
838
         * @return true if using the system class loader, else false.
839
         */
840
        public boolean isUseSystemClassLoader() {
841
                return useSystemClassLoader;
1✔
842
        }
843
        
844
        public static void setRefreshingContext(boolean refreshingContext) {
845
                ServiceContext.refreshingContext = refreshingContext;
1✔
846
        }
1✔
847
        
848
        /**
849
         * Should be called <b>right before</b> any spring context refresh This forces all calls to
850
         * getService to wait until <code>doneRefreshingContext</code> is called
851
         */
852
        public void startRefreshingContext() {
853
                synchronized (refreshingContextLock) {
1✔
854
                        log.info("Refreshing Context");
1✔
855
                        setRefreshingContext(true);
1✔
856
                }
1✔
857
        }
1✔
858
        
859
        /**
860
         * Should be called <b>right after</b> any spring context refresh This wakes up all calls to
861
         * getService that were waiting because <code>startRefreshingContext</code> was called
862
         */
863
        public void doneRefreshingContext() {
864
                synchronized (refreshingContextLock) {
1✔
865
                        log.info("Done refreshing Context");
1✔
866
                        setRefreshingContext(false);
1✔
867
                        refreshingContextLock.notifyAll();
1✔
868
                }
1✔
869
        }
1✔
870
        
871
        /**
872
         * Returns true/false whether startRefreshingContext() has been called without a subsequent call
873
         * to doneRefreshingContext() yet. All methods involved in starting/stopping a module should
874
         * call this if a service method is needed -- otherwise a deadlock will occur.
875
         *
876
         * @return true/false whether the services are currently blocking waiting for a call to
877
         *         doneRefreshingContext()
878
         */
879
        public boolean isRefreshingContext() {
880
                return refreshingContext;
1✔
881
        }
882
        
883
        /**
884
         * Retrieves all Beans which have been registered in the Spring {@link ApplicationContext} that
885
         * match the given object type (including subclasses).
886
         * <p>
887
         * <b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i> check nested
888
         * beans which might match the specified type as well.
889
         *
890
         * @see ApplicationContext#getBeansOfType(Class)
891
         * @param type the type of Bean to retrieve from the Spring {@link ApplicationContext}
892
         * @return a List of all registered Beans that are valid instances of the passed type
893
         * @since 1.5
894
         * <strong>Should</strong> return a list of all registered beans of the passed type
895
         * <strong>Should</strong> return beans registered in a module
896
         * <strong>Should</strong> return an empty list if no beans have been registered of the passed type
897
         */
898
        
899
        public <T> List<T> getRegisteredComponents(Class<T> type) {
900
                Map<String, T> m = getRegisteredComponents(applicationContext, type);
1✔
901
                log.trace("getRegisteredComponents({}) = {}", type, m);
1✔
902
                return new ArrayList<>(m.values());
1✔
903
        }
904
        
905
        /**
906
         * Retrieves a bean that match the given type (including subclasses) and name.
907
         *
908
         * @param beanName the name of registered bean to retrieve
909
         * @param type the type of bean to retrieve 
910
         * @return bean of passed type
911
         *
912
         * @since 1.9.4
913
         */
914
        public <T> T getRegisteredComponent(String beanName, Class<T> type) throws APIException {
915
                try {
916
                        return applicationContext.getBean(beanName, type);
1✔
917
                }
918
                catch (BeansException beanException) {
1✔
919
                        throw new APIException("Error during getting registered component", beanException);
1✔
920
                }
921
        }
922
        
923
        /**
924
         * Private method which returns all components registered in a Spring applicationContext of a
925
         * given type This method recurses through each parent ApplicationContext
926
         *
927
         * @param context - The applicationContext to check
928
         * @param type - The type of component to retrieve
929
         * @return all components registered in a Spring applicationContext of a given type
930
         */
931
        private <T> Map<String, T> getRegisteredComponents(ApplicationContext context, Class<T> type) {
932
                Map<String, T> registeredComponents = context.getBeansOfType(type);
1✔
933
                log.trace("getRegisteredComponents({}, {}) = {}", context, type, registeredComponents);
1✔
934
                Map<String, T> components = new HashMap<>(registeredComponents);
1✔
935
                if (context.getParent() != null) {
1✔
936
                        components.putAll(getRegisteredComponents(context.getParent(), type));
1✔
937
                }
938
                return components;
1✔
939
        }
940
        
941
        /**
942
         * @param applicationContext the applicationContext to set
943
         */
944
        @Override
945
        public void setApplicationContext(ApplicationContext applicationContext) {
946
                this.applicationContext = applicationContext;
1✔
947
        }
1✔
948
        
949
        public ApplicationContext getApplicationContext() {
950
                return applicationContext;
1✔
951
        }
952
        
953
        /**
954
         * Calls the {@link OpenmrsService#onStartup()} method for an instance implementing the
955
         * {@link OpenmrsService} interface.
956
         *
957
         * @param openmrsService instance implementing the {@link OpenmrsService} interface.
958
         * @param classString the full instance class name including the package name.
959
         * @since 1.9
960
         */
961
        private void runOpenmrsServiceOnStartup(final OpenmrsService openmrsService, final String classString) {
962
                OpenmrsThreadPoolHolder.threadExecutor.execute(() -> {
1✔
963
                        try {
964
                                synchronized (refreshingContextLock) {
1✔
965
                                        //Need to wait for application context to finish refreshing otherwise we get into trouble.
966
                                        while (refreshingContext) {
1✔
967
                                                log.debug("Waiting to get service: {} while the context is being refreshed", classString);
×
968
        
969
                                                refreshingContextLock.wait();
×
970
        
971
                                                log.debug("Finished waiting to get service {} while the context was being refreshed", classString);
×
972
                                        }
973
                                }
1✔
974
        
975
                                Daemon.runStartupForService(openmrsService, Daemon.callerKey());
1✔
976
                        }
977
                                catch (InterruptedException e) {
×
978
                                log.warn("Refresh lock was interrupted while waiting to run OpenmrsService.onStartup() for "
×
979
                                        + classString, e);
980
                        }
1✔
981
                });
1✔
982
        }
1✔
983
        
984
        /**
985
         * Gets a list of services implementing the {@link OpenmrsService} interface, for a given
986
         * module.
987
         *
988
         * @param modulePackage the module's package name.
989
         * @return the list of service instances.
990
         * @since 1.9
991
         */
992
        public List<OpenmrsService> getModuleOpenmrsServices(String modulePackage) {
993
                List<OpenmrsService> openmrsServices = new ArrayList<>();
1✔
994
                
995
                for (Entry<String, OpenmrsService> entry : moduleOpenmrsServices.entrySet()) {
1✔
996
                        if (entry.getKey().startsWith(modulePackage)) {
1✔
997
                                openmrsServices.add(entry.getValue());
1✔
998
                        }
999
                }
1✔
1000
                
1001
                return openmrsServices;
1✔
1002
        }
1003
        
1004
        /**
1005
         * Gets the visit service.
1006
         *
1007
         * @return visit service.
1008
         * @since 1.9
1009
         **/
1010
        public VisitService getVisitService() {
1011
                return getService(VisitService.class);
1✔
1012
        }
1013
        
1014
        /**
1015
         * Sets the visit service.
1016
         *
1017
         * @param visitService the visitService to set
1018
         * @since 1.9
1019
         **/
1020
        public void setVisitService(VisitService visitService) {
1021
                setService(VisitService.class, visitService);
1✔
1022
        }
1✔
1023
        
1024
        /**
1025
         * Gets the provider service.
1026
         *
1027
         * @return provider service.
1028
         * @since 1.9
1029
         **/
1030
        
1031
        public ProviderService getProviderService() {
1032
                return getService(ProviderService.class);
1✔
1033
        }
1034
        
1035
        /**
1036
         * Sets the provider service.
1037
         *
1038
         * @param providerService the providerService to set
1039
         * @since 1.9
1040
         **/
1041
        public void setProviderService(ProviderService providerService) {
1042
                setService(ProviderService.class, providerService);
1✔
1043
        }
1✔
1044
        
1045
        /**
1046
         * Gets the datatype service
1047
         *
1048
         * @return custom datatype service
1049
         * @since 1.9
1050
         */
1051
        public DatatypeService getDatatypeService() {
1052
                return getService(DatatypeService.class);
1✔
1053
        }
1054
        
1055
        /**
1056
         * Sets the datatype service
1057
         *
1058
         * @param datatypeService the datatypeService to set
1059
         * @since 1.9
1060
         */
1061
        public void setDatatypeService(DatatypeService datatypeService) {
1062
                setService(DatatypeService.class, datatypeService);
1✔
1063
        }
1✔
1064

1065
        /**
1066
         * Clears entire API cache.
1067
         * 
1068
         * @since 2.8.0
1069
         */
1070
        public void clearEntireApiCache() {
1071
                CacheManager apiCacheManager = getRegisteredComponent("apiCacheManager", CacheManager.class);
1✔
1072
                apiCacheManager.getCacheNames().forEach(cacheName -> apiCacheManager.getCache(cacheName).invalidate());
1✔
1073
        }
1✔
1074
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc