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

openmrs / openmrs-core / 30399801741

28 Jul 2026 09:13PM UTC coverage: 64.333% (+0.2%) from 64.136%
30399801741

push

github

web-flow
Fix Mockito inline mocks on JDK 8 and 11 (#6403)

24619 of 38268 relevant lines covered (64.33%)

0.64 hits per line

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

62.92
/api/src/main/java/org/openmrs/api/cache/CacheConfig.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.cache;
11

12
import java.io.ByteArrayInputStream;
13
import java.io.IOException;
14
import java.io.InputStream;
15
import java.net.URL;
16
import java.nio.charset.StandardCharsets;
17
import java.util.ArrayList;
18
import java.util.LinkedHashMap;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22

23
import org.apache.commons.lang.StringUtils;
24
import org.apache.commons.text.CaseUtils;
25
import org.infinispan.commons.dataconversion.MediaType;
26
import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
27
import org.infinispan.configuration.parsing.ParserRegistry;
28
import org.infinispan.manager.DefaultCacheManager;
29
import org.infinispan.remoting.transport.jgroups.JGroupsTransport;
30
import org.infinispan.spring.embedded.provider.SpringEmbeddedCacheManager;
31
import org.jgroups.JChannel;
32
import org.jgroups.protocols.TCP;
33
import org.jgroups.protocols.TP;
34
import org.jgroups.protocols.TUNNEL;
35
import org.jgroups.protocols.UDP;
36
import org.slf4j.Logger;
37
import org.slf4j.LoggerFactory;
38
import org.springframework.beans.factory.annotation.Value;
39
import org.springframework.cache.CacheManager;
40
import org.springframework.context.annotation.Bean;
41
import org.springframework.context.annotation.Configuration;
42
import org.springframework.core.io.Resource;
43
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
44
import org.springframework.core.io.support.ResourcePatternResolver;
45
import org.yaml.snakeyaml.DumperOptions;
46
import org.yaml.snakeyaml.Yaml;
47

48
/**
49
 * CacheConfig provides a cache manager for the @Cacheable annotation and uses Infinispan under the hood.
50
 * The config of Infinispan is loaded from infinispan-api-local.xml/infinispan-api.xml and can be customized by 
51
 * providing a different file through the cache_config property. It is expected for the config to contain a template 
52
 * named "entity" to be used to create caches.
53
 * <p>
54
 * Caches can be added by modules through a cache-api.yaml file in the classpath.
55
 * The file shall contain only the <b>caches</b> element as defined in Infinispan docs at 
56
 * <a href="https://infinispan.org/docs/13.0.x/titles/configuring/configuring.html#multiple_caches">multiple caches</a> 
57
 * <p>
58
 * Please note the underlying implementation changed from ehcache to Infinispan since 2.8.x 
59
 * to support replicated/distributed caches.
60
 */
61
@Configuration
62
public class CacheConfig {
1✔
63
        private final static Logger log = LoggerFactory.getLogger(CacheConfig.class);
1✔
64
        
65
        @Value("${cache.type:local}")
66
        private String cacheType;
67
        
68
        @Value("${cache.config:}")
69
        private String cacheConfig;
70
        
71
        @Value("${cache.stack:}")
72
        private String cacheStack;
73
        
74
        @Value("${cache.api.bind.port:}")
75
        private String apiCacheBindPort;
76
        
77
        private String jChannelConfig;
78

79
        @Bean(name = "apiCacheManager", destroyMethod = "stop")
80
        public SpringEmbeddedCacheManager apiCacheManager() throws Exception {
81
                if (StringUtils.isBlank(cacheConfig)) {
1✔
82
                        String local = "local".equalsIgnoreCase(cacheType.trim()) ? "-local" : "";
1✔
83
                        cacheConfig = "infinispan-api" + local + ".xml";
1✔
84
                }
85

86
                ParserRegistry parser = new ParserRegistry();
1✔
87
                ConfigurationBuilderHolder baseConfigBuilder = parser.parseFile(cacheConfig);
1✔
88
                if(cacheType.trim().equals("cluster")) {
1✔
89
                        jChannelConfig = getJChannelConfig(cacheStack);
×
90
                        JChannel jchannel = new JChannel(jChannelConfig);
×
91
                        Class<? extends TP> protocolClass = TCP.class;
×
92
                        if (cacheStack.trim().isEmpty() || cacheStack.trim().equals("udp")) {
×
93
                                protocolClass = UDP.class;
×
94
                        } else if(cacheStack.trim().equals("tunnel")) {
×
95
                                protocolClass = TUNNEL.class;
×
96
                        }
97
                        TP protocol = jchannel.getProtocolStack().findProtocol(protocolClass);
×
98
                        if (StringUtils.isBlank(apiCacheBindPort)) {
×
99
                                String hibernateCacheBindPort = System.getProperty("jgroups.bind.port");
×
100
                                if (hibernateCacheBindPort == null) {
×
101
                                        hibernateCacheBindPort = "7800";
×
102
                                }
103
                                apiCacheBindPort = String.valueOf(Integer.parseInt(hibernateCacheBindPort) + 1);
×
104
                        }
105
                        protocol.setBindPort(Integer.parseInt(apiCacheBindPort));
×
106
                        JGroupsTransport transport = new JGroupsTransport(jchannel);
×
107
                        baseConfigBuilder.getGlobalConfigurationBuilder().transport().clusterName("infinispan-api-cluster").transport(transport);
×
108
                }
109
                // Determine cache type based on loaded template for "entity"
110
                String cacheType = baseConfigBuilder.getNamedConfigurationBuilders().get("entity").build().elementName();
1✔
111
                cacheType = StringUtils.removeEnd(cacheType, "-configuration");
1✔
112
                cacheType = CaseUtils.toCamelCase(cacheType, false, '-');
1✔
113

114
                DumperOptions options = new DumperOptions();
1✔
115
                options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
1✔
116
                options.setPrettyFlow(true);
1✔
117
                Yaml yaml = new Yaml(options);
1✔
118

119
                for (URL configFile : getCacheConfigurations()) {
1✔
120
                        // Apply cache type for caches using the 'entity' template 
121
                        // and add the 'infinispan.cacheContainer.caches' parent.
122
                        // Skip already defined caches.
123
                        InputStream fullConfig = buildFullConfig(yaml, configFile,
1✔
124
                                baseConfigBuilder.getNamedConfigurationBuilders().keySet(), cacheType);
1✔
125
                        parser.parse(fullConfig, baseConfigBuilder, null,
1✔
126
                                MediaType.APPLICATION_YAML);
127
                }
1✔
128
                
129
                DefaultCacheManager cacheManager = new DefaultCacheManager(baseConfigBuilder, true);
1✔
130
                return new SpringEmbeddedCacheManager(cacheManager);
1✔
131
        }
132

133
        private static InputStream buildFullConfig(Yaml yaml, URL configFile, Set<String> templateNames, String cacheType) throws IOException {
134
                Map<String, Object> loadedConfig = yaml.load(configFile.openStream());
1✔
135

136
                Map<String, Object> config = new LinkedHashMap<>();
1✔
137
                Map<String, Object> cacheContainer = new LinkedHashMap<>();
1✔
138
                Map<String, Object> caches = new LinkedHashMap<>();
1✔
139
                Map<String, Object> cacheList = new LinkedHashMap<>();
1✔
140
                config.put("infinispan", cacheContainer);
1✔
141
                cacheContainer.put("cacheContainer", caches);
1✔
142

143
                @SuppressWarnings("unchecked")
144
                Map<String, Object> loadedCaches = (Map<String, Object>) loadedConfig.get("caches");
1✔
145
                for (Map.Entry<String, Object> entry : loadedCaches.entrySet()) {
1✔
146
                        if (templateNames.contains(entry.getKey())) {
1✔
147
                                // already defined by the base configuration; don't redefine it
148
                                continue;
1✔
149
                        }
150
                        @SuppressWarnings("unchecked")
151
                        Map<String, Object> value = (Map<String, Object>) entry.getValue();
1✔
152
                        if (templateNames.contains(value.get("configuration"))) {
1✔
153
                                // A cache referencing a base template must be wrapped in the mode's cache type
154
                                // (local-cache/invalidation-cache) so it inherits that template's settings.
155
                                Map<Object, Object> cache = new LinkedHashMap<>();
1✔
156
                                cache.put(cacheType, value);
1✔
157
                                cacheList.put(entry.getKey(), cache);
1✔
158
                        } else {
1✔
159
                                cacheList.put(entry.getKey(), value);
×
160
                        }
161
                }
1✔
162
                if (!cacheList.isEmpty()) {
1✔
163
                        caches.put("caches", cacheList);
1✔
164
                }
165

166
                String configDump = yaml.dump(config);
1✔
167
                return new ByteArrayInputStream(configDump.getBytes(StandardCharsets.UTF_8));
1✔
168
        }
169

170
        public List<URL> getCacheConfigurations() {
171
                Resource[] configResources;
172
                try {
173
                        ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
1✔
174
                        configResources = patternResolver.getResources("classpath*:cache-api.yaml");
1✔
175
                } catch (IOException e) {
×
176
                        throw new IllegalStateException("Unable to find cache configurations", e);
×
177
                }
1✔
178

179
                List<URL> files = new ArrayList<>();
1✔
180
                for (Resource configResource : configResources) {
1✔
181
                        try {
182
                                URL file = configResource.getURL();
1✔
183
                                files.add(file);
1✔
184
                        } catch (IOException e) {
×
185
                                log.error("Failed to get cache config file: {}", configResource, e);
×
186
                        }
1✔
187
                }
188

189
                return files;
1✔
190
        }
191
        
192
        public String getJChannelConfig(String cacheStack) {
193
                String jChannelConfig;
194
                switch (cacheStack.trim()) {
1✔
195
                        case "tcp":
196
                                jChannelConfig = "default-configs/default-jgroups-tcp.xml";
×
197
                                break;
×
198
                        case "kubernetes":
199
                                jChannelConfig = "default-configs/default-jgroups-kubernetes.xml";
×
200
                                break;
×
201
                        case "google":
202
                                jChannelConfig = "default-configs/default-jgroups-google.xml";
×
203
                                break;
×
204
                        case "tunnel":
205
                                jChannelConfig = "default-configs/default-jgroups-tunnel.xml";
×
206
                                break;
×
207
                        case "ec2":
208
                                jChannelConfig = "default-configs/default-jgroups-ec2.xml";
×
209
                                break;
×
210
                        case "azure":
211
                                jChannelConfig = "default-configs/default-jgroups-azure.xml";
×
212
                                break;
×
213
                        default:
214
                                jChannelConfig = "default-configs/default-jgroups-udp.xml";
1✔
215
                }
216
                return jChannelConfig;
1✔
217
        }
218
}
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