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

openmrs / openmrs-core / 31024361752

05 Aug 2026 04:14PM UTC coverage: 64.336% (-0.09%) from 64.422%
31024361752

push

github

ibacher
TRUNK-6687: Follow-up: The test here makes incorrect assumptions on the cap

With duplicate records (what we're testing here) it is possible to need
to page over slightly more results than are in the page. This is expected
and was causing this test to fail in some circumstances where index order
became relevant

24620 of 38268 relevant lines covered (64.34%)

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