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

openmrs / openmrs-core / 15205088843

23 May 2025 07:43AM UTC coverage: 65.083% (+0.01%) from 65.069%
15205088843

push

github

rkorytkowski
TRUNK-6300: Adding Windows test, cleaning up logs, adjusting variable name

2 of 2 new or added lines in 2 files covered. (100.0%)

336 existing lines in 18 files now uncovered.

23379 of 35922 relevant lines covered (65.08%)

0.65 hits per line

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

89.47
/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.spring.embedded.provider.SpringEmbeddedCacheManager;
30
import org.slf4j.Logger;
31
import org.slf4j.LoggerFactory;
32
import org.springframework.beans.factory.FactoryBean;
33
import org.springframework.beans.factory.annotation.Value;
34
import org.springframework.context.annotation.Bean;
35
import org.springframework.context.annotation.Configuration;
36
import org.springframework.core.io.Resource;
37
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
38
import org.springframework.core.io.support.ResourcePatternResolver;
39
import org.yaml.snakeyaml.DumperOptions;
40
import org.yaml.snakeyaml.Yaml;
41

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

65
        @Bean(name = "apiCacheManager")
66
        public SpringEmbeddedCacheManager apiCacheManager() throws IOException {
67
                if (StringUtils.isBlank(cacheConfig)) {
1✔
68
                        String local = "local".equalsIgnoreCase(cacheType.trim()) ? "-local" : "";
1✔
69
                        cacheConfig = "infinispan-api" + local + ".xml";
1✔
70
                }
71

72
                ParserRegistry parser = new ParserRegistry();
1✔
73
                ConfigurationBuilderHolder baseConfigBuilder = parser.parseFile(cacheConfig);
1✔
74

75
                // Determine cache type based on loaded template for "entity"
76
                String cacheType = baseConfigBuilder.getNamedConfigurationBuilders().get("entity").build().elementName();
1✔
77
                cacheType = StringUtils.removeEnd(cacheType, "-configuration");
1✔
78
                cacheType = CaseUtils.toCamelCase(cacheType, false, '-');
1✔
79

80
                DumperOptions options = new DumperOptions();
1✔
81
                options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
1✔
82
                options.setPrettyFlow(true);
1✔
83
                Yaml yaml = new Yaml(options);
1✔
84

85
                for (URL configFile : getCacheConfigurations()) {
1✔
86
                        // Apply cache type for caches using the 'entity' template 
87
                        // and add the 'infinispan.cacheContainer.caches' parent.
88
                        // Skip already defined caches.
89
                        InputStream fullConfig = buildFullConfig(yaml, configFile,
1✔
90
                                baseConfigBuilder.getNamedConfigurationBuilders().keySet(), cacheType);
1✔
91
                        parser.parse(fullConfig, baseConfigBuilder, null,
1✔
92
                                MediaType.APPLICATION_YAML);
93
                }
1✔
94
                
95
                DefaultCacheManager cacheManager = new DefaultCacheManager(baseConfigBuilder, true);
1✔
96
                return new SpringEmbeddedCacheManager(cacheManager);
1✔
97
        }
98

99
        private static InputStream buildFullConfig(Yaml yaml, URL configFile, Set<String> skipCaches, String cacheType) throws IOException {
100
                Map<String, Object> loadedConfig = yaml.load(configFile.openStream());
1✔
101

102
                Map<String, Object> config = new LinkedHashMap<>();
1✔
103
                Map<String, Object> cacheContainer = new LinkedHashMap<>();
1✔
104
                Map<String, Object> caches = new LinkedHashMap<>();
1✔
105
                Map<String, Object> cacheList = new LinkedHashMap<>();
1✔
106
                config.put("infinispan", cacheContainer);
1✔
107
                cacheContainer.put("cacheContainer", caches);
1✔
108

109
                @SuppressWarnings("unchecked")
110
                Map<String, Object> loadedCaches = (Map<String, Object>) loadedConfig.get("caches");
1✔
111
                for (Map.Entry<String, Object> entry : loadedCaches.entrySet()) {
1✔
112
                        @SuppressWarnings("unchecked")
113
                        Map<String, Object> value = (Map<String, Object>) entry.getValue();
1✔
114
                        if ("entity".equals(value.get("configuration"))) {
1✔
115
                                Map<Object, Object> cache = new LinkedHashMap<>();
1✔
116
                                cache.put(cacheType, value);
1✔
117
                                if (!skipCaches.contains(entry.getKey())) {
1✔
118
                                        cacheList.put(entry.getKey(), cache);
1✔
119
                                }
120
                        } else {
1✔
UNCOV
121
                                if (!skipCaches.contains(entry.getKey())) {
×
UNCOV
122
                                        cacheList.put(entry.getKey(), value);
×
123
                                }
124
                        }
125
                }
1✔
126
                if (!cacheList.isEmpty()) {
1✔
127
                        caches.put("caches", cacheList);
1✔
128
                }
129

130
                String configDump = yaml.dump(config);
1✔
131
                return new ByteArrayInputStream(configDump.getBytes(StandardCharsets.UTF_8));
1✔
132
        }
133

134
        public List<URL> getCacheConfigurations() {
135
                Resource[] configResources;
136
                try {
137
                        ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
1✔
138
                        configResources = patternResolver.getResources("classpath*:cache-api.yaml");
1✔
UNCOV
139
                } catch (IOException e) {
×
UNCOV
140
                        throw new IllegalStateException("Unable to find cache configurations", e);
×
141
                }
1✔
142

143
                List<URL> files = new ArrayList<>();
1✔
144
                for (Resource configResource : configResources) {
1✔
145
                        try {
146
                                URL file = configResource.getURL();
1✔
147
                                files.add(file);
1✔
UNCOV
148
                        } catch (IOException e) {
×
UNCOV
149
                                log.error("Failed to get cache config file: {}", configResource, e);
×
150
                        }
1✔
151
                }
152

153
                return files;
1✔
154
        }
155
        
156
}
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