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

openmrs / openmrs-module-webservices.rest / 17263834497

27 Aug 2025 10:12AM UTC coverage: 68.44% (-1.5%) from 69.946%
17263834497

push

github

web-flow
RESTWS-988: Update to OpenMRS Platform 2.7.x and Support Java 21 (#670)

* Update to Platform 2.7.x

* Merge 2.5 and 2.7

* Merge 2.4 and 2.7

* Merge omod and omod-2.7

* Update ChangePasswordController1_8

* Move InitPathMatcher2_4 to the correct package

* Address review comments

* Add the licence header

* Revert AnimalResource_1_11

* Update tests

* Update tests

* Update AnimalClassResourceLegacy

* Remove setCurrentOpenMRSVersion method

* Revert formatting changes

* Revert formatting changes

* Revert formatting changes

* Merge omod and commons

* Revert "Merge omod and commons"

This reverts commit c68ef20d3.

* Move RestServiceImplTest to omod

* Remove AnimalClassResourceLegacy.java

* fix doSearch_shouldProcessIncludeAll

* update doSearch_shouldProcessIncludeAll

* update doSearch_shouldProcessIncludeAll

* Use hamcrest in ProviderController2_0Test

10 of 15 new or added lines in 5 files covered. (66.67%)

239 existing lines in 7 files now uncovered.

9572 of 13986 relevant lines covered (68.44%)

0.68 hits per line

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

15.52
/omod-common/src/main/java/org/openmrs/module/webservices/rest/web/api/impl/RestServiceImpl.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.module.webservices.rest.web.api.impl;
11

12
import java.io.IOException;
13
import java.util.ArrayList;
14
import java.util.HashMap;
15
import java.util.HashSet;
16
import java.util.Iterator;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Map.Entry;
20
import java.util.Set;
21

22
import org.apache.commons.lang.StringUtils;
23
import org.hibernate.proxy.HibernateProxy;
24
import org.openmrs.api.APIException;
25
import org.openmrs.module.ModuleUtil;
26
import org.openmrs.module.webservices.rest.web.OpenmrsClassScanner;
27
import org.openmrs.module.webservices.rest.web.RestConstants;
28
import org.openmrs.module.webservices.rest.web.annotation.SubResource;
29
import org.openmrs.module.webservices.rest.web.api.RestHelperService;
30
import org.openmrs.module.webservices.rest.web.api.RestService;
31
import org.openmrs.module.webservices.rest.web.representation.CustomRepresentation;
32
import org.openmrs.module.webservices.rest.web.representation.NamedRepresentation;
33
import org.openmrs.module.webservices.rest.web.representation.Representation;
34
import org.openmrs.module.webservices.rest.web.resource.api.Resource;
35
import org.openmrs.module.webservices.rest.web.resource.api.SearchConfig;
36
import org.openmrs.module.webservices.rest.web.resource.api.SearchHandler;
37
import org.openmrs.module.webservices.rest.web.resource.api.SearchParameter;
38
import org.openmrs.module.webservices.rest.web.resource.api.SearchQuery;
39
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceHandler;
40
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingSubclassHandler;
41
import org.openmrs.module.webservices.rest.web.response.InvalidSearchException;
42
import org.openmrs.module.webservices.rest.web.response.UnknownResourceException;
43
import org.openmrs.util.OpenmrsConstants;
44

45
/**
46
 * Default implementation of the {@link RestService}
47
 */
48
public class RestServiceImpl implements RestService {
49
        
50
        volatile Map<String, ResourceDefinition> resourceDefinitionsByNames;
51
        
52
        volatile Map<Class<?>, Resource> resourcesBySupportedClasses;
53
        
54
        private volatile Map<CompositeSearchHandlerKeyValue, Set<SearchHandler>> searchHandlersByParameter;
55
        
56
        private volatile Map<CompositeSearchHandlerKeyValue, SearchHandler> searchHandlersByIds;
57
        
58
        private volatile Map<String, Set<SearchHandler>> searchHandlersByResource;
59
        
60
        private volatile List<SearchHandler> allSearchHandlers;
61
        
62
        private RestHelperService restHelperService;
63
        
64
        private OpenmrsClassScanner openmrsClassScanner;
65
        
66
        public RestHelperService getRestHelperService() {
67
                return restHelperService;
×
68
        }
69
        
70
        public void setRestHelperService(RestHelperService restHelperService) {
71
                this.restHelperService = restHelperService;
1✔
72
        }
1✔
73
        
74
        public OpenmrsClassScanner getOpenmrsClassScanner() {
75
                return openmrsClassScanner;
×
76
        }
77
        
78
        public void setOpenmrsClassScanner(OpenmrsClassScanner openmrsClassScanner) {
79
                this.openmrsClassScanner = openmrsClassScanner;
1✔
80
        }
1✔
81
        
82
        public RestServiceImpl() {
1✔
83
        }
1✔
84
        
85
        static class ResourceDefinition {
86
                
87
                public Resource resource;
88
                
89
                public int order;
90
                
UNCOV
91
                public ResourceDefinition(Resource resource, int order) {
×
UNCOV
92
                        this.resource = resource;
×
UNCOV
93
                        this.order = order;
×
UNCOV
94
                }
×
95
                
96
        }
97
        
98
        /**
99
         * Wraps {@code Resource} name and an additional string-based key into a composite key.
100
         */
101
        private static class CompositeSearchHandlerKeyValue {
102
                
103
                public final String supportedResource;
104
                
105
                public final String secondKey;
106
                
107
                public final String secondKeyValue;
108
                
UNCOV
109
                public CompositeSearchHandlerKeyValue(String supportedResource, String additionalKeyProperty) {
×
UNCOV
110
                        this.supportedResource = supportedResource;
×
UNCOV
111
                        this.secondKey = additionalKeyProperty;
×
UNCOV
112
                        this.secondKeyValue = null;
×
UNCOV
113
                }
×
114
                
115
                public CompositeSearchHandlerKeyValue(String supportedResource, String additionalKeyProperty,
UNCOV
116
                    String additionalKeyPropertyValue) {
×
UNCOV
117
                        this.supportedResource = supportedResource;
×
UNCOV
118
                        this.secondKey = additionalKeyProperty;
×
UNCOV
119
                        this.secondKeyValue = additionalKeyPropertyValue;
×
UNCOV
120
                }
×
121
                
122
                @Override
123
                public boolean equals(Object o) {
UNCOV
124
                        if (this == o)
×
125
                                return true;
×
UNCOV
126
                        if (o == null || getClass() != o.getClass())
×
127
                                return false;
×
128
                        
UNCOV
129
                        CompositeSearchHandlerKeyValue that = (CompositeSearchHandlerKeyValue) o;
×
130
                        
UNCOV
131
                        if (!supportedResource.equals(that.supportedResource))
×
132
                                return false;
×
UNCOV
133
                        if (!secondKey.equals(that.secondKey))
×
134
                                return false;
×
UNCOV
135
                        return secondKeyValue != null ? secondKeyValue.equals(that.secondKeyValue) : that.secondKeyValue == null;
×
136
                        
137
                }
138
                
139
                @Override
140
                public int hashCode() {
UNCOV
141
                        int result = supportedResource.hashCode();
×
UNCOV
142
                        result = 31 * result + secondKey.hashCode();
×
UNCOV
143
                        result = 31 * result + (secondKeyValue != null ? secondKeyValue.hashCode() : 0);
×
UNCOV
144
                        return result;
×
145
                }
146
        }
147
        
148
        private void initializeResources() {
149
                if (resourceDefinitionsByNames != null) {
1✔
150
                        return;
1✔
151
                }
152
                
153
                Map<String, ResourceDefinition> tempResourceDefinitionsByNames = new HashMap<String, ResourceDefinition>();
1✔
154
                Map<Class<?>, Resource> tempResourcesBySupportedClasses = new HashMap<Class<?>, Resource>();
1✔
155
                
156
                List<Class<? extends Resource>> resources;
157
                try {
158
                        resources = openmrsClassScanner.getClasses(Resource.class, true);
1✔
159
                }
UNCOV
160
                catch (IOException e) {
×
UNCOV
161
                        throw new APIException("Cannot access REST resources", e);
×
162
                }
1✔
163
                
164
                for (Class<? extends Resource> resource : resources) {
1✔
165
                        ResourceMetadata resourceMetadata = getResourceMetadata(resource);
1✔
166
                        if (resourceMetadata == null)
1✔
167
                                continue;
1✔
168
                        
UNCOV
169
                        if (isResourceToBeAdded(resourceMetadata, tempResourceDefinitionsByNames.get(resourceMetadata.getName()))) {
×
UNCOV
170
                                Resource newResource = newResource(resource);
×
171
                                
UNCOV
172
                                tempResourceDefinitionsByNames.put(resourceMetadata.getName(), new ResourceDefinition(newResource,
×
UNCOV
173
                                        resourceMetadata.getOrder()));
×
UNCOV
174
                                tempResourcesBySupportedClasses.put(resourceMetadata.getSupportedClass(), newResource);
×
175
                        }
UNCOV
176
                }
×
177
                
178
                resourcesBySupportedClasses = tempResourcesBySupportedClasses;
1✔
179
                resourceDefinitionsByNames = tempResourceDefinitionsByNames;
1✔
180
        }
1✔
181
        
182
        /**
183
         * Determines whether a {@code Resource} should be added to the cache.
184
         * 
185
         * @param resourceMetadata the resource metadata of the resource to be added
186
         * @param existingResourceDefinition the resource definition of resource
187
         * @return true if the resource should be added and false otherwise
188
         */
189
        private boolean isResourceToBeAdded(ResourceMetadata resourceMetadata, ResourceDefinition existingResourceDefinition) {
190
                
UNCOV
191
                if (existingResourceDefinition == null) {
×
UNCOV
192
                        return true;
×
193
                }
UNCOV
194
                if (existingResourceDefinition.order == resourceMetadata.getOrder()) {
×
UNCOV
195
                        throw new IllegalStateException("Two resources with the same name (" + resourceMetadata.getName()
×
196
                                + ") must not have the same order");
197
                }
198
                
UNCOV
199
                return existingResourceDefinition.order >= resourceMetadata.getOrder();
×
200
        }
201
        
202
        /**
203
         * Gets {@code ResourceMetadata} from a {@code Resource} classes annotations.
204
         * 
205
         * @param resource the resource to get the metadata from
206
         * @return the metadata of a resource
207
         */
208
        private ResourceMetadata getResourceMetadata(Class<? extends Resource> resource) {
209
                ResourceMetadata resourceMetadata;
210
                
211
                org.openmrs.module.webservices.rest.web.annotation.Resource resourceAnnotation = resource
1✔
212
                        .getAnnotation(org.openmrs.module.webservices.rest.web.annotation.Resource.class);
1✔
213
                if (resourceAnnotation == null) {
1✔
214
                        SubResource subresourceAnnotation = resource.getAnnotation(SubResource.class);
1✔
215
                        if (subresourceAnnotation == null
1✔
UNCOV
216
                                || !isOpenmrsVersionInVersions(subresourceAnnotation.supportedOpenmrsVersions())) {
×
217
                                return null;
1✔
218
                        }
UNCOV
219
                        org.openmrs.module.webservices.rest.web.annotation.Resource parentResourceAnnotation = subresourceAnnotation
×
UNCOV
220
                                .parent().getAnnotation(org.openmrs.module.webservices.rest.web.annotation.Resource.class);
×
UNCOV
221
                        if (parentResourceAnnotation == null) {
×
222
                                return null;
×
223
                        }
UNCOV
224
                        resourceMetadata = new ResourceMetadata(parentResourceAnnotation.name() + "/" + subresourceAnnotation.path(),
×
UNCOV
225
                                subresourceAnnotation.supportedClass(), subresourceAnnotation.order());
×
UNCOV
226
                } else {
×
UNCOV
227
                        if (!isOpenmrsVersionInVersions(resourceAnnotation.supportedOpenmrsVersions())) {
×
UNCOV
228
                                return null;
×
229
                        }
UNCOV
230
                        resourceMetadata = new ResourceMetadata(resourceAnnotation.name(), resourceAnnotation.supportedClass(),
×
UNCOV
231
                                resourceAnnotation.order());
×
232
                }
UNCOV
233
                return resourceMetadata;
×
234
        }
235
        
236
        private static class ResourceMetadata {
237
                
238
                private final String name;
239
                
240
                private final Class<?> supportedClass;
241
                
242
                private final int order;
243
                
UNCOV
244
                public ResourceMetadata(String name, Class<?> supportedClass, int order) {
×
UNCOV
245
                        this.name = name;
×
UNCOV
246
                        this.supportedClass = supportedClass;
×
UNCOV
247
                        this.order = order;
×
UNCOV
248
                }
×
249
                
250
                public String getName() {
UNCOV
251
                        return name;
×
252
                }
253
                
254
                public Class<?> getSupportedClass() {
UNCOV
255
                        return supportedClass;
×
256
                }
257
                
258
                public int getOrder() {
UNCOV
259
                        return order;
×
260
                }
261
        }
262
        
263
        /**
264
         * Checks if OpenMRS version is in given array of versions.
265
         * 
266
         * @param versions the array of versions to be checked for the openmrs version
267
         * @return true if the openmrs version is in versions and false otherwise
268
         */
269
        private boolean isOpenmrsVersionInVersions(String[] versions) {
270
                
UNCOV
271
                if (versions.length == 0) {
×
272
                        return false;
×
273
                }
274
                
UNCOV
275
                boolean result = false;
×
UNCOV
276
                for (String version : versions) {
×
UNCOV
277
                        if (ModuleUtil.matchRequiredVersions(OpenmrsConstants.OPENMRS_VERSION_SHORT, version)) {
×
UNCOV
278
                                result = true;
×
UNCOV
279
                                break;
×
280
                        }
281
                }
UNCOV
282
                return result;
×
283
        }
284
        
285
        private void initializeSearchHandlers() {
UNCOV
286
                if (searchHandlersByIds != null) {
×
287
                        return;
×
288
                }
289
                
UNCOV
290
                Map<CompositeSearchHandlerKeyValue, SearchHandler> tempSearchHandlersByIds = new HashMap<CompositeSearchHandlerKeyValue, SearchHandler>();
×
UNCOV
291
                Map<CompositeSearchHandlerKeyValue, Set<SearchHandler>> tempSearchHandlersByParameters = new HashMap<CompositeSearchHandlerKeyValue, Set<SearchHandler>>();
×
UNCOV
292
                Map<String, Set<SearchHandler>> tempSearchHandlersByResource = new HashMap<String, Set<SearchHandler>>();
×
293
                
UNCOV
294
                List<SearchHandler> allSearchHandlers = restHelperService.getRegisteredSearchHandlers();
×
UNCOV
295
                for (SearchHandler searchHandler : allSearchHandlers) {
×
UNCOV
296
                        addSearchHandler(tempSearchHandlersByIds, tempSearchHandlersByParameters, tempSearchHandlersByResource,
×
297
                            searchHandler);
UNCOV
298
                }
×
UNCOV
299
                this.allSearchHandlers = allSearchHandlers;
×
UNCOV
300
                searchHandlersByParameter = tempSearchHandlersByParameters;
×
UNCOV
301
                searchHandlersByIds = tempSearchHandlersByIds;
×
UNCOV
302
                searchHandlersByResource = tempSearchHandlersByResource;
×
UNCOV
303
        }
×
304
        
305
        private void addSearchHandler(Map<CompositeSearchHandlerKeyValue, SearchHandler> tempSearchHandlersByIds,
306
                Map<CompositeSearchHandlerKeyValue, Set<SearchHandler>> tempSearchHandlersByParameters,
307
                Map<String, Set<SearchHandler>> tempSearchHandlersByResource, SearchHandler searchHandler) {
UNCOV
308
                for (String supportedVersion : searchHandler.getSearchConfig().getSupportedOpenmrsVersions()) {
×
UNCOV
309
                        if (ModuleUtil.matchRequiredVersions(OpenmrsConstants.OPENMRS_VERSION_SHORT, supportedVersion)) {
×
UNCOV
310
                                addSupportedSearchHandler(tempSearchHandlersByIds, tempSearchHandlersByParameters, searchHandler);
×
UNCOV
311
                                addSearchHandlerToResourceMap(tempSearchHandlersByResource, searchHandler);
×
312
                        }
UNCOV
313
                }
×
UNCOV
314
        }
×
315
        
316
        private void addSupportedSearchHandler(Map<CompositeSearchHandlerKeyValue, SearchHandler> tempSearchHandlersByIds,
317
                Map<CompositeSearchHandlerKeyValue, Set<SearchHandler>> tempSearchHandlersByParameters,
318
                SearchHandler searchHandler) {
UNCOV
319
                CompositeSearchHandlerKeyValue searchHanlderIdKey = new CompositeSearchHandlerKeyValue(searchHandler
×
UNCOV
320
                        .getSearchConfig().getSupportedResource(), searchHandler.getSearchConfig().getId());
×
UNCOV
321
                SearchHandler previousSearchHandler = tempSearchHandlersByIds.put(searchHanlderIdKey, searchHandler);
×
UNCOV
322
                if (previousSearchHandler != null) {
×
UNCOV
323
                        SearchConfig config = searchHandler.getSearchConfig();
×
UNCOV
324
                        throw new IllegalStateException("Two search handlers (" + searchHandler.getClass() + ", "
×
UNCOV
325
                                + previousSearchHandler.getClass() + ") for the same resource (" + config.getSupportedResource()
×
UNCOV
326
                                + ") must not have the same ID (" + config.getId() + ")");
×
327
                }
328
                
UNCOV
329
                addSearchHandlerToParametersMap(tempSearchHandlersByParameters, searchHandler);
×
UNCOV
330
        }
×
331
        
332
        private void addSearchHandlerToParametersMap(
333
                Map<CompositeSearchHandlerKeyValue, Set<SearchHandler>> tempSearchHandlersByParameters,
334
                SearchHandler searchHandler) {
335
                
UNCOV
336
                for (SearchQuery searchQueries : searchHandler.getSearchConfig().getSearchQueries()) {
×
UNCOV
337
                        Set<SearchParameter> parameters = new HashSet<SearchParameter>(searchQueries.getRequiredParameters());
×
UNCOV
338
                        parameters.addAll(searchQueries.getOptionalParameters());
×
339
                        
UNCOV
340
                        for (SearchParameter parameter : parameters) {
×
UNCOV
341
                                CompositeSearchHandlerKeyValue parameterKey = new CompositeSearchHandlerKeyValue(searchHandler
×
UNCOV
342
                                        .getSearchConfig().getSupportedResource(), parameter.getName(), parameter.getValue());
×
UNCOV
343
                                Set<SearchHandler> list = tempSearchHandlersByParameters.get(parameterKey);
×
UNCOV
344
                                if (list == null) {
×
UNCOV
345
                                        list = new HashSet<SearchHandler>();
×
UNCOV
346
                                        tempSearchHandlersByParameters.put(parameterKey, list);
×
347
                                }
UNCOV
348
                                list.add(searchHandler);
×
UNCOV
349
                        }
×
UNCOV
350
                }
×
UNCOV
351
        }
×
352
        
353
        private void addSearchHandlerToResourceMap(Map<String, Set<SearchHandler>> tempSearchHandlersByResource,
354
                SearchHandler searchHandler) {
UNCOV
355
                SearchConfig config = searchHandler.getSearchConfig();
×
UNCOV
356
                Set<SearchHandler> handlers = tempSearchHandlersByResource.get(config.getSupportedResource());
×
UNCOV
357
                if (handlers == null) {
×
UNCOV
358
                        handlers = new HashSet<SearchHandler>();
×
UNCOV
359
                        tempSearchHandlersByResource.put(config.getSupportedResource(), handlers);
×
360
                }
UNCOV
361
                handlers.add(searchHandler);
×
UNCOV
362
        }
×
363
        
364
        /**
365
         * @see org.openmrs.module.webservices.rest.web.api.RestService#getRepresentation(java.lang.String)
366
         * <strong>Should</strong> return default representation if given null
367
         * <strong>Should</strong> return default representation if given string is empty
368
         * <strong>Should</strong> return reference representation if given string matches the ref representation
369
         *         constant
370
         * <strong>Should</strong> return default representation if given string matches the default representation
371
         *         constant
372
         * <strong>Should</strong> return full representation if given string matches the full representation constant
373
         * <strong>Should</strong> return an instance of custom representation if given string starts with the custom
374
         *         representation prefix
375
         * <strong>Should</strong> return an instance of named representation for given string if it is not empty and
376
         *         does not match any other case
377
         */
378
        @Override
379
        public Representation getRepresentation(String requested) {
380
                if (StringUtils.isEmpty(requested)) {
1✔
UNCOV
381
                        return Representation.DEFAULT;
×
382
                }
383
                
384
                if (RestConstants.REPRESENTATION_REF.equals(requested)) {
1✔
385
                        return Representation.REF;
1✔
386
                } else if (RestConstants.REPRESENTATION_DEFAULT.equals(requested)) {
1✔
387
                        return Representation.DEFAULT;
1✔
388
                } else if (RestConstants.REPRESENTATION_FULL.equals(requested)) {
1✔
389
                        return Representation.FULL;
1✔
390
                } else if (requested.startsWith(RestConstants.REPRESENTATION_CUSTOM_PREFIX)) {
1✔
UNCOV
391
                        return new CustomRepresentation(requested.replace(RestConstants.REPRESENTATION_CUSTOM_PREFIX, ""));
×
392
                }
393
                
394
                return new NamedRepresentation(requested);
1✔
395
        }
396
        
397
        /**
398
         * @see org.openmrs.module.webservices.rest.web.api.RestService#getResourceByName(String)
399
         * <strong>Should</strong> return resource for given name
400
         * <strong>Should</strong> return resource for given name and ignore unannotated resources
401
         * <strong>Should</strong> fail if failed to get resource classes
402
         * <strong>Should</strong> fail if resource for given name cannot be found
403
         * <strong>Should</strong> fail if resource for given name does not support the current openmrs version
404
         * <strong>Should</strong> return subresource for given name
405
         * <strong>Should</strong> fail if subresource for given name does not support the current openmrs version
406
         * <strong>Should</strong> fail if two resources with same name and order are found for given name
407
         * <strong>Should</strong> return resource with lower order value if two resources with the same name are found
408
         *         for given name
409
         */
410
        @Override
411
        public Resource getResourceByName(String name) throws APIException {
UNCOV
412
                initializeResources();
×
413
                
UNCOV
414
                ResourceDefinition resourceDefinition = resourceDefinitionsByNames.get(name);
×
UNCOV
415
                if (resourceDefinition == null) {
×
UNCOV
416
                        throw new UnknownResourceException("Unknown resource: " + name);
×
417
                } else {
UNCOV
418
                        return resourceDefinition.resource;
×
419
                }
420
        }
421
        
422
        /**
423
         * @see org.openmrs.module.webservices.rest.web.api.RestService#getResourceBySupportedClass(Class)
424
         * <strong>Should</strong> return resource supporting given class and current openmrs version
425
         * <strong>Should</strong> fail if no resource supporting given class and current openmrs version was found
426
         * <strong>Should</strong> fail if no resource supporting given class was found
427
         * <strong>Should</strong> return resource supporting superclass of given class if given class is a hibernate
428
         *         proxy
429
         * <strong>Should</strong> return resource supporting superclass of given class if no resource supporting given
430
         *         class was found
431
         * <strong>Should</strong> return resource supporting direct superclass of given class if no resource supporting
432
         *         given class was found but multiple resources supporting multiple superclasses exist
433
         * <strong>Should</strong> fail if failed to get resource classes
434
         * <strong>Should</strong> fail if two resources with same name and order are found for given class
435
         * <strong>Should</strong> return resource with lower order value if two resources with the same name are found
436
         *         for given class
437
         */
438
        @Override
439
        public Resource getResourceBySupportedClass(Class<?> resourceClass) throws APIException {
440
                initializeResources();
1✔
441
                
442
                if (HibernateProxy.class.isAssignableFrom(resourceClass)) {
1✔
UNCOV
443
                        resourceClass = resourceClass.getSuperclass();
×
444
                }
445
                
446
                Resource resource = resourcesBySupportedClasses.get(resourceClass);
1✔
447
                
448
                if (resource == null) {
1✔
449
                        Entry<Class<?>, Resource> bestResourceEntry = null;
1✔
450
                        
451
                        for (Entry<Class<?>, Resource> resourceEntry : resourcesBySupportedClasses.entrySet()) {
1✔
UNCOV
452
                                if (resourceEntry.getKey().isAssignableFrom(resourceClass) && (bestResourceEntry == null
×
UNCOV
453
                                        || bestResourceEntry.getKey().isAssignableFrom(resourceEntry.getKey()))) {
×
UNCOV
454
                                        bestResourceEntry = resourceEntry;
×
455
                                }
UNCOV
456
                        }
×
457
                        
458
                        if (bestResourceEntry != null) {
1✔
UNCOV
459
                                resource = bestResourceEntry.getValue();
×
460
                        }
461
                }
462
                
463
                if (resource == null) {
1✔
464
                        throw new APIException("Unknown resource: " + resourceClass);
1✔
465
                } else {
UNCOV
466
                        return resource;
×
467
                }
468
        }
469
        
470
        /**
471
         * @throws InstantiationException
472
         */
473
        private Resource newResource(Class<? extends Resource> resourceClass) {
474
                try {
UNCOV
475
                        return resourceClass.newInstance();
×
476
                }
UNCOV
477
                catch (Exception ex) {
×
UNCOV
478
                        throw new APIException("Failed to instantiate " + resourceClass, ex);
×
479
                }
480
        }
481
        
482
        /**
483
         * Returns a search handler, which supports the given resource and the map of parameters and
484
         * values.
485
         * <p>
486
         * A {@code SearchHandler} is selected according to following steps (in this order):
487
         * <ul>
488
         * <li>Lookup a {@code SearchHandler} based on its {@code id} ({@code SearchConfig#id}) if
489
         * specified in given {@code parameters}. This lookup can fail if no or two
490
         * {@code SearchHandler}'s is/are found for given {@code id} and {@code resourceName}.</li>
491
         * <li>Lookup a {@code SearchHandler} based on given {@code parameters} if no {@code id} is
492
         * specified. The lookup returns the {@code SearcHandler} supporting all requested
493
         * {@code parameters} and with {@code parameters} satisfying the {@code SearchHandler}'s
494
         * {@code SearchConfig}'s required parameters. This lookup can fail if more than 1
495
         * {@code SearchHandler} satisfies the requirements mentioned before.</li>
496
         * </ul>
497
         * If no {@code SearchHandler} is found, {@code NULL} is returned.
498
         * </p>
499
         * 
500
         * @see org.openmrs.module.webservices.rest.web.api.RestService#getSearchHandler(java.lang.String,
501
         *      java.util.Map)
502
         * <strong>Should</strong> return search handler matching id set in given parameters
503
         * <strong>Should</strong> fail if parameters contain a search handler id which cannot be found
504
         * <strong>Should</strong> fail if two search handlers for the same resource have the same id
505
         * <strong>Should</strong> return null if parameters do not contain a search handler id and no other non special
506
         *         request parameters
507
         * <strong>Should</strong> return search handler providing all request parameters and parameters satisfying its
508
         *         required parameters
509
         * <strong>Should</strong> return null if given parameters are missing a parameter required by search handlers
510
         *         eligible for given resource name and parameters
511
         * <strong>Should</strong> return default search handler if two search handlers match given resource and parameters and no search handler
512
         *                id is specified and one of the matching search handlers is the default
513
         * <strong>Should</strong> fail if two search handlers match given resource and parameters and no search handler
514
         *         id is specified and neither matching search hander is the default
515
         * <strong>Should</strong> return null if a non special request parameter in given parameters cannot be found in
516
         *         any search handler
517
         * <strong>Should</strong> return null if no search handler is found for given resource name
518
         * <strong>Should</strong> return null if no search handler is found for current openmrs version
519
         */
520
        @Override
521
        public SearchHandler getSearchHandler(String resourceName, Map<String, String[]> parameters) throws APIException {
UNCOV
522
                initializeSearchHandlers();
×
523
                
UNCOV
524
                Set<SearchParameter> searchParameters = new HashSet<SearchParameter>();
×
525
                
UNCOV
526
                for (Map.Entry<String, String[]> parameter : parameters.entrySet()) {
×
UNCOV
527
                        if (!RestConstants.SPECIAL_REQUEST_PARAMETERS.contains(parameter.getKey())
×
UNCOV
528
                                || RestConstants.REQUEST_PROPERTY_FOR_TYPE.equals(parameter.getKey())) {
×
UNCOV
529
                                searchParameters.add(new SearchParameter(parameter.getKey(), parameter.getValue()[0]));
×
530
                        }
UNCOV
531
                }
×
532
                
UNCOV
533
                String[] searchIds = parameters.get(RestConstants.REQUEST_PROPERTY_FOR_SEARCH_ID);
×
UNCOV
534
                if (searchIds != null && searchIds.length > 0) {
×
UNCOV
535
                        SearchHandler searchHandler = searchHandlersByIds.get(new CompositeSearchHandlerKeyValue(resourceName,
×
536
                                searchIds[0]));
UNCOV
537
                        if (searchHandler == null) {
×
UNCOV
538
                                throw new InvalidSearchException("The search with id '" + searchIds[0] + "' for '" + resourceName
×
539
                                        + "' resource is not recognized");
540
                        } else {
UNCOV
541
                                return searchHandler;
×
542
                        }
543
                }
544
                
UNCOV
545
                Set<SearchHandler> candidateSearchHandlers = null;
×
UNCOV
546
                for (SearchParameter param : searchParameters) {
×
UNCOV
547
                        Set<SearchHandler> searchHandlers = searchHandlersByParameter.get(new CompositeSearchHandlerKeyValue(
×
UNCOV
548
                                resourceName, param.getName(), param.getValue()));
×
UNCOV
549
                        if (searchHandlers == null) {
×
UNCOV
550
                                searchHandlers = searchHandlersByParameter.get(new CompositeSearchHandlerKeyValue(resourceName, param
×
UNCOV
551
                                        .getName()));
×
UNCOV
552
                                if (searchHandlers == null)
×
UNCOV
553
                                        return null; //Missing parameter so there's no handler.
×
554
                        }
UNCOV
555
                        if (candidateSearchHandlers == null) {
×
UNCOV
556
                                candidateSearchHandlers = new HashSet<SearchHandler>();
×
UNCOV
557
                                candidateSearchHandlers.addAll(searchHandlers);
×
558
                        } else {
559
                                //Eliminate candidate search handlers that do not include all parameters
UNCOV
560
                                candidateSearchHandlers.retainAll(searchHandlers);
×
561
                        }
UNCOV
562
                }
×
563
                
UNCOV
564
                if (candidateSearchHandlers == null) {
×
UNCOV
565
                        return null;
×
566
                } else {
UNCOV
567
                        eliminateCandidateSearchHandlersWithMissingRequiredParameters(candidateSearchHandlers, searchParameters);
×
568
                        
UNCOV
569
                        if (candidateSearchHandlers.isEmpty()) {
×
UNCOV
570
                                return null;
×
UNCOV
571
                        } else if (candidateSearchHandlers.size() == 1) {
×
UNCOV
572
                                return candidateSearchHandlers.iterator().next();
×
573
                        }
574
                        // if multiple, return default
UNCOV
575
                        for (SearchHandler candidateSearchHandler : candidateSearchHandlers) {
×
UNCOV
576
                                if ("default".equals(candidateSearchHandler.getSearchConfig().getId())) {
×
UNCOV
577
                                        return candidateSearchHandler;
×
578
                                }
UNCOV
579
                        }
×
580

581
                        // multiple and no default, throw exception
UNCOV
582
                        List<String> candidateSearchHandlerIds = new ArrayList<String>();
×
UNCOV
583
                        for (SearchHandler candidateSearchHandler : candidateSearchHandlers) {
×
UNCOV
584
                                candidateSearchHandlerIds.add(RestConstants.REQUEST_PROPERTY_FOR_SEARCH_ID + "="
×
UNCOV
585
                                                + candidateSearchHandler.getSearchConfig().getId());
×
UNCOV
586
                        }
×
UNCOV
587
                        throw new InvalidSearchException("The search is ambiguous. Please specify "
×
UNCOV
588
                                        + StringUtils.join(candidateSearchHandlerIds, " or "));
×
589

590
                }
591
        }
592
        
593
        /**
594
         * Eliminate search handlers with at least one required parameter that is not provided in
595
         * {@code searchParameters}.
596
         * 
597
         * @param candidateSearchHandlers the search handlers to filter for required parameters
598
         * @param searchParameters the search parameters to be checked against search handlers required
599
         *            parameters
600
         */
601
        private void eliminateCandidateSearchHandlersWithMissingRequiredParameters(Set<SearchHandler> candidateSearchHandlers,
602
                Set<SearchParameter> searchParameters) {
UNCOV
603
                Iterator<SearchHandler> it = candidateSearchHandlers.iterator();
×
UNCOV
604
                while (it.hasNext()) {
×
UNCOV
605
                        SearchHandler candidateSearchHandler = it.next();
×
UNCOV
606
                        boolean remove = true;
×
607
                        
UNCOV
608
                        for (SearchQuery candidateSearchQueries : candidateSearchHandler.getSearchConfig().getSearchQueries()) {
×
UNCOV
609
                                Set<SearchParameter> requiredParameters = new HashSet<SearchParameter>(
×
UNCOV
610
                                        candidateSearchQueries.getRequiredParameters());
×
611
                                
UNCOV
612
                                Iterator<SearchParameter> iterator = requiredParameters.iterator();
×
UNCOV
613
                                while (iterator.hasNext()) {
×
UNCOV
614
                                        SearchParameter requiredParameter = iterator.next();
×
UNCOV
615
                                        for (SearchParameter param : searchParameters) {
×
UNCOV
616
                                                if (requiredParameter.getValue() == null) {
×
UNCOV
617
                                                        if (requiredParameter.getName().equals(param.getName())) {
×
UNCOV
618
                                                                iterator.remove();
×
619
                                                        }
620
                                                } else {
621
                                                        if (requiredParameter.equals(param)) {
×
622
                                                                iterator.remove();
×
623
                                                        }
624
                                                }
UNCOV
625
                                        }
×
UNCOV
626
                                }
×
UNCOV
627
                                if (requiredParameters.isEmpty()) {
×
UNCOV
628
                                        remove = false;
×
UNCOV
629
                                        break;
×
630
                                }
UNCOV
631
                        }
×
632
                        
UNCOV
633
                        if (remove) {
×
UNCOV
634
                                it.remove();
×
635
                        }
UNCOV
636
                }
×
UNCOV
637
        }
×
638
        
639
        /**
640
         * @see org.openmrs.module.webservices.rest.web.api.RestService#getResourceHandlers()
641
         * <strong>Should</strong> return list of delegating resource handlers including subclass handlers
642
         * <strong>Should</strong> return list with delegating resource with lower order value if two resources with the
643
         *         same name are found for given name
644
         * <strong>Should</strong> fail if failed to get resource classes
645
         * <strong>Should</strong> fail if two resources with same name and order are found for a class
646
         */
647
        @Override
648
        public List<DelegatingResourceHandler<?>> getResourceHandlers() throws APIException {
UNCOV
649
                initializeResources();
×
650
                
UNCOV
651
                List<DelegatingResourceHandler<?>> resourceHandlers = new ArrayList<DelegatingResourceHandler<?>>();
×
652
                
UNCOV
653
                for (Resource resource : resourcesBySupportedClasses.values()) {
×
UNCOV
654
                        if (resource instanceof DelegatingResourceHandler) {
×
UNCOV
655
                                resourceHandlers.add((DelegatingResourceHandler<?>) resource);
×
656
                        }
UNCOV
657
                }
×
658
                
UNCOV
659
                List<DelegatingSubclassHandler> subclassHandlers = restHelperService.getRegisteredRegisteredSubclassHandlers();
×
UNCOV
660
                for (DelegatingSubclassHandler subclassHandler : subclassHandlers) {
×
UNCOV
661
                        resourceHandlers.add(subclassHandler);
×
UNCOV
662
                }
×
663
                
UNCOV
664
                return resourceHandlers;
×
665
        }
666
        
667
        /**
668
         * @see org.openmrs.module.webservices.rest.web.api.RestService#getAllSearchHandlers()
669
         * <strong>Should</strong> return all search handlers if search handlers have been initialized
670
         * <strong>Should</strong> return null if search handlers have not been initialized
671
         */
672
        public List<SearchHandler> getAllSearchHandlers() {
673
                
UNCOV
674
                return allSearchHandlers;
×
675
        }
676
        
677
        /**
678
         * @see org.openmrs.module.webservices.rest.web.api.RestService#getSearchHandlers(java.lang.String)
679
         * <strong>Should</strong> return search handlers for given resource name
680
         * <strong>Should</strong> return null if no search handler is found for given resource name
681
         * <strong>Should</strong> return null if no search handler is found for current openmrs version
682
         * <strong>Should</strong> return null given null
683
         * <strong>Should</strong> fail if two search handlers for the same resource have the same id
684
         */
685
        @Override
686
        public Set<SearchHandler> getSearchHandlers(String resourceName) {
UNCOV
687
                if (searchHandlersByResource == null) {
×
UNCOV
688
                        initializeSearchHandlers();
×
689
                }
UNCOV
690
                return searchHandlersByResource.get(resourceName);
×
691
        }
692
        
693
        /**
694
         * @see RestService#initialize()
695
         * <strong>Should</strong> initialize resources and search handlers
696
         * <strong>Should</strong> clear cached resources and search handlers and reinitialize them
697
         * <strong>Should</strong> fail if failed to get resource classes
698
         * <strong>Should</strong> fail if failed to instantiate a resource
699
         * <strong>Should</strong> fail if two resources with same name and order are found
700
         * <strong>Should</strong> fail if two search handlers for the same resource have the same id
701
         */
702
        @Override
703
        public void initialize() {
704
                
705
                // first clear out any existing values
UNCOV
706
                resourceDefinitionsByNames = null;
×
UNCOV
707
                resourcesBySupportedClasses = null;
×
UNCOV
708
                searchHandlersByIds = null;
×
UNCOV
709
                searchHandlersByParameter = null;
×
UNCOV
710
                searchHandlersByResource = null;
×
711
                
UNCOV
712
                initializeResources();
×
UNCOV
713
                initializeSearchHandlers();
×
UNCOV
714
        }
×
715
}
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