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

openmrs / openmrs-core / 22781579539

06 Mar 2026 08:53PM UTC coverage: 65.392%. Remained the same
22781579539

push

github

ibacher
Fix the Mockito hack

Previously, the way we were controlling mockito resulted in an inconsistent
dependency tree. This fixes the profile so we only need one and updates
the version property instead of the raw dependency.

23842 of 36460 relevant lines covered (65.39%)

0.65 hits per line

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

85.29
/api/src/main/java/org/openmrs/util/HandlerUtil.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.util;
11

12
import java.util.ArrayList;
13
import java.util.Comparator;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.WeakHashMap;
17

18
import org.openmrs.annotation.Handler;
19
import org.openmrs.api.APIException;
20
import org.openmrs.api.context.Context;
21
import org.slf4j.Logger;
22
import org.slf4j.LoggerFactory;
23
import org.springframework.context.ApplicationListener;
24
import org.springframework.context.event.ContextRefreshedEvent;
25
import org.springframework.stereotype.Component;
26

27
/**
28
 * Utility class that provides useful methods for working with classes that are annotated with the
29
 * {@link Handler} annotation
30
 * 
31
 * @since 1.5
32
 */
33
@Component
34
public class HandlerUtil implements ApplicationListener<ContextRefreshedEvent> {
1✔
35
        
36
        private static final Logger log = LoggerFactory.getLogger(HandlerUtil.class);
1✔
37
        
38
        private static volatile Map<Key, List<?>> cachedHandlers = new WeakHashMap<>();
1✔
39
        
40
        private static class Key {
41
                
42
                public final Class<?> handlerType;
43
                
44
                public final Class<?> type;
45
                
46
                public Key(Class<?> handlerType, Class<?> type) {
1✔
47
                        this.handlerType = handlerType;
1✔
48
                        this.type = type;
1✔
49
                }
1✔
50
                
51
                @Override
52
                public int hashCode() {
53
                        final int prime = 31;
1✔
54
                        int result = 1;
1✔
55
                        result = prime * result + ((handlerType == null) ? 0 : handlerType.hashCode());
1✔
56
                        result = prime * result + ((type == null) ? 0 : type.hashCode());
1✔
57
                        return result;
1✔
58
                }
59
                
60
                @Override
61
                public boolean equals(Object obj) {
62
                        if (this == obj) {
1✔
63
                                return true;
×
64
                        }
65
                        if (obj == null) {
1✔
66
                                return false;
×
67
                        }
68
                        if (getClass() != obj.getClass()) {
1✔
69
                                return false;
×
70
                        }
71
                        Key other = (Key) obj;
1✔
72
                        if (handlerType == null) {
1✔
73
                                if (other.handlerType != null) {
×
74
                                        return false;
×
75
                                }
76
                        } else if (!handlerType.equals(other.handlerType)) {
1✔
77
                                return false;
×
78
                        }
79
                        if (type == null) {
1✔
80
                                return other.type == null;
×
81
                        } else {
82
                                return type.equals(other.type);
1✔
83
                        }
84
                }
85
                
86
        }
87
        
88
        public static void clearCachedHandlers() {
89
                cachedHandlers = new WeakHashMap<>();
1✔
90
        }
1✔
91
        
92
        /**
93
         * Retrieves a List of all registered components from the Context that are of the passed
94
         * handlerType and one or more of the following is true:
95
         * <ul>
96
         * <li>The handlerType is annotated as a {@link Handler} that supports the passed type</li>
97
         * <li>The passed type is null - this effectively returns all components of the passed
98
         * handlerType</li>
99
         * </ul>
100
         * The returned handlers are ordered in the list based upon the order property.
101
         * 
102
         * @param handlerType Indicates the type of class to return
103
         * @param type Indicates the type that the given handlerType must support (or null for any)
104
         * @return a List of all matching Handlers for the given parameters, ordered by Handler#order
105
         * <strong>Should</strong> return a list of all classes that can handle the passed type
106
         * <strong>Should</strong> return classes registered in a module
107
         * <strong>Should</strong> return an empty list if no classes can handle the passed type
108
         */
109
        public static <H, T> List<H> getHandlersForType(Class<H> handlerType, Class<T> type) {
110
                List<?> list = cachedHandlers.get(new Key(handlerType, type));
1✔
111
                if (list != null) {
1✔
112
                        return (List<H>) list;
1✔
113
                }
114
                
115
                List<H> handlers = new ArrayList<>();
1✔
116
                
117
                // First get all registered components of the passed class
118
                log.debug("Getting handlers of type " + handlerType + (type == null ? "" : " for class " + type.getName()));
1✔
119
                for (H handler : Context.getRegisteredComponents(handlerType)) {
1✔
120
                        Handler handlerAnnotation = handler.getClass().getAnnotation(Handler.class);
1✔
121
                        // Only consider those that have been annotated as Handlers
122
                        if (handlerAnnotation != null) {
1✔
123
                                // If no type is passed in return all handlers
124
                                if (type == null) {
1✔
125
                                        log.debug("Found handler " + handler.getClass());
1✔
126
                                        handlers.add(handler);
1✔
127
                                }
128
                                // Otherwise, return all handlers that support the passed type
129
                                else {
130
                                        for (int i = 0; i < handlerAnnotation.supports().length; i++) {
1✔
131
                                                Class<?> clazz = handlerAnnotation.supports()[i];
1✔
132
                                                if (clazz.isAssignableFrom(type)) {
1✔
133
                                                        log.debug("Found handler: " + handler.getClass());
1✔
134
                                                        handlers.add(handler);
1✔
135
                                                }
136
                                        }
137
                                }
138
                        }
139
                }
1✔
140
                
141
                // Return the list of handlers based on the order specified in the Handler annotation
142
                handlers.sort(Comparator.comparing(o -> getOrderOfHandler(o.getClass())));
1✔
143
                
144
                Map<Key, List<?>> newCachedHandlers = new WeakHashMap<>(cachedHandlers);
1✔
145
                newCachedHandlers.put(new Key(handlerType, type), handlers);
1✔
146
                cachedHandlers = newCachedHandlers;
1✔
147
                
148
                return handlers;
1✔
149
        }
150
        
151
        /**
152
         * Retrieves the preferred Handler for a given handlerType and type. A <em>preferred</em>
153
         * handler is the Handler that has the lowest defined <em>order</em> attribute in it's
154
         * annotation. If multiple Handlers are found for the passed parameters at the lowest specified
155
         * order, then an APIException is thrown.
156
         * 
157
         * @param handlerType the class that is an annotated {@link Handler} to retrieve
158
         * @param type the class that the annotated {@link Handler} must support
159
         * @return the class of the passed handlerType with the lowest configured order
160
         * <strong>Should</strong> return the preferred handler for the passed handlerType and type
161
         * <strong>Should</strong> throw a APIException if no handler is found
162
         * <strong>Should</strong> throw a APIException if multiple preferred handlers are found
163
         * <strong>Should</strong> should return patient validator for patient
164
         * <strong>Should</strong> should return person validator for person
165
         */
166
        public static <H, T> H getPreferredHandler(Class<H> handlerType, Class<T> type) {
167
                
168
                if (handlerType == null || type == null) {
1✔
169
                        throw new IllegalArgumentException("You must specify both a handlerType and a type");
×
170
                }
171
                List<H> handlers = getHandlersForType(handlerType, type);
1✔
172
                if (handlers == null || handlers.isEmpty()) {
1✔
173
                        throw new APIException("handler.type.not.found", new Object[] { handlerType, type });
1✔
174
                }
175
                
176
                if (handlers.size() > 1) {
1✔
177
                        int order1 = getOrderOfHandler(handlers.get(0).getClass());
1✔
178
                        int order2 = getOrderOfHandler(handlers.get(1).getClass());
1✔
179
                        if (order1 == order2) {
1✔
180
                                throw new APIException("handler.type.multiple", new Object[] { handlerType, type });
×
181
                        }
182
                }
183
                
184
                return handlers.get(0);
1✔
185
        }
186
        
187
        /**
188
         * Utility method to return the order attribute of the {@link Handler} annotation on the passed
189
         * class. If the passed class does not have a {@link Handler} annotation, a RuntimeException is
190
         * thrown
191
         * 
192
         * @param handlerClass
193
         * @return the order attribute value
194
         */
195
        public static Integer getOrderOfHandler(Class<?> handlerClass) {
196
                Handler annotation = handlerClass.getAnnotation(Handler.class);
1✔
197
                if (annotation == null) {
1✔
198
                        throw new APIException("class.not.annotated.as.handler", new Object[] { handlerClass });
×
199
                }
200
                return annotation.order();
1✔
201
        }
202
        
203
        @Override
204
        public void onApplicationEvent(ContextRefreshedEvent event) {
205
                clearCachedHandlers();
1✔
206
        }
1✔
207
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc