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

openmrs / openmrs-core / 22296336943

23 Feb 2026 07:12AM UTC coverage: 63.442% (-0.05%) from 63.493%
22296336943

push

github

web-flow
TRUNK-6469: Add support for automatic initialization of Envers audit tables (#5468) (#5831)

14 of 60 new or added lines in 3 files covered. (23.33%)

246 existing lines in 4 files now uncovered.

23202 of 36572 relevant lines covered (63.44%)

0.63 hits per line

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

78.89
/api/src/main/java/org/openmrs/api/db/hibernate/HibernateSessionFactoryBean.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.db.hibernate;
11

12
import java.io.IOException;
13
import java.io.InputStream;
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.Collections;
17
import java.util.HashMap;
18
import java.util.HashSet;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Map.Entry;
22
import java.util.Properties;
23
import java.util.Set;
24

25
import org.hibernate.HibernateException;
26
import org.hibernate.Interceptor;
27
import org.hibernate.boot.Metadata;
28
import org.hibernate.engine.spi.SessionFactoryImplementor;
29
import org.hibernate.integrator.spi.Integrator;
30
import org.hibernate.service.ServiceRegistry;
31
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
32
import org.jspecify.annotations.NonNull;
33
import org.openmrs.api.APIException;
34
import org.openmrs.api.cache.CacheConfig;
35
import org.openmrs.api.context.Context;
36
import org.openmrs.module.Module;
37
import org.openmrs.module.ModuleFactory;
38
import org.openmrs.util.EnversAuditTableInitializer;
39
import org.openmrs.util.OpenmrsUtil;
40
import org.slf4j.Logger;
41
import org.slf4j.LoggerFactory;
42
import org.slf4j.MarkerFactory;
43
import org.springframework.beans.factory.annotation.Autowired;
44
import org.springframework.beans.factory.annotation.Value;
45
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
46

47
public class HibernateSessionFactoryBean extends LocalSessionFactoryBean implements Integrator {
1✔
48
        
49
        private static final Logger log = LoggerFactory.getLogger(HibernateSessionFactoryBean.class);
1✔
50
        
51
        protected Set<String> mappingResources = new HashSet<>();
1✔
52
        
53
        /**
54
         * @since 1.9.2, 1.10
55
         */
56
        protected Set<String> packagesToScan = new HashSet<>();
1✔
57
        
58
        // @since 1.6.3, 1.7.2, 1.8.0, 1.9
59
        protected ChainingInterceptor chainingInterceptor = new ChainingInterceptor();
1✔
60
        
61
        // @since 1.6.3, 1.7.2, 1.8.0, 1.9
62
        // This will be sorted on keys before being used
63
        @Autowired(required = false)
1✔
64
        public Map<String, Interceptor> interceptors = new HashMap<>();
65

66
        @Value("${cache.type:local}")
67
        private String cacheType;
68
        
69
        @Value(("${cache.stack:}"))
70
        private String cacheStack;
71
        
72
        private Metadata metadata;
73
        
74
        @Autowired
75
        private CacheConfig cacheConfig;
76

77
        /**
78
         * Collect the mapping resources for future use because the mappingResources object is defined
79
         * as 'private' instead of 'protected'
80
         */
81
        @Override
82
        public void setMappingResources(String @NonNull ... mappingResources) {
83
                Collections.addAll(this.mappingResources, mappingResources);
1✔
84
                
85
                super.setMappingResources(this.mappingResources.toArray(new String[] {}));
1✔
86
        }
1✔
87
        
88
        /**
89
         * Collect packages to scan that are set in core and for tests in modules.
90
         * <p>
91
         * It adds to the set instead of overwriting it with each call.
92
         */
93
        @Override
94
        public void setPackagesToScan(String @NonNull ... packagesToScan) {
95
                this.packagesToScan.addAll(Arrays.asList(packagesToScan));
1✔
96
                
97
                super.setPackagesToScan(this.packagesToScan.toArray(new String[0]));
1✔
98
        }
1✔
99
        
100
        public Set<String> getModuleMappingResources() {
101
                for (Module mod : ModuleFactory.getStartedModules()) {
1✔
102
                        mappingResources.addAll(mod.getMappingFiles());
×
103
                }
×
104
                return mappingResources;
1✔
105
        }
106
        
107
        /**
108
         * Gets packages with mapped classes from all modules.
109
         *
110
         * @return the set of packages with mapped classes
111
         * @since 1.9.2, 1.10
112
         */
113
        public Set<String> getModulePackagesWithMappedClasses() {
114
                Set<String> packages = new HashSet<>();
1✔
115
                for (Module module : ModuleFactory.getStartedModules()) {
1✔
116
                        packages.addAll(module.getPackagesWithMappedClasses());
×
117
                }
×
118
                return packages;
1✔
119
        }
120
        
121
        /**
122
         * Overridden to populate mappings from modules.
123
         */
124
        @Override
125
        public void afterPropertiesSet() throws IOException {
126
                log.debug("Configuring hibernate sessionFactory properties");
1✔
127
                Properties config = getHibernateProperties();
1✔
128
                
129
                Properties moduleProperties = Context.getConfigProperties();
1✔
130
                
131
                // override or initialize config properties with module-provided ones
132
                for (Map.Entry<Object, Object> entry : moduleProperties.entrySet()) {
1✔
133
                        Object key = entry.getKey();
×
134
                        String prop = (String) key;
×
135
                        String value = (String) entry.getValue();
×
NEW
136
                        log.trace("Setting module property: {}:{}", prop, value);
×
137
                        config.setProperty(prop, value);
×
138
                        if (!prop.startsWith("hibernate")) {
×
139
                                config.setProperty("hibernate." + prop, value);
×
140
                        }
141
                }
×
142
                
143
                Properties properties = Context.getRuntimeProperties();
1✔
144
                
145
                // loop over runtime properties and override each in the configuration
146
                for (Map.Entry<Object, Object> entry : properties.entrySet()) {
1✔
147
                        Object key = entry.getKey();
1✔
148
                        String prop = (String) key;
1✔
149
                        String value = (String) entry.getValue();
1✔
150
                        log.trace("Setting property: {}:{}", prop, value);
1✔
151
                        config.setProperty(prop, value);
1✔
152
                        if (!prop.startsWith("hibernate")) {
1✔
153
                                config.setProperty("hibernate." + prop, value);
1✔
154
                        }
155
                }
1✔
156
                
157
                // load in the default hibernate properties
158
                try {
159
                        InputStream propertyStream = getClass().getResourceAsStream("/hibernate.default.properties");
1✔
160
                        Properties props = new Properties();
1✔
161
                        
162
                        OpenmrsUtil.loadProperties(props, propertyStream);
1✔
163
                        propertyStream.close();
1✔
164
                        
165
                        String jChannelConfig=cacheConfig.getJChannelConfig(cacheStack);
1✔
166
                        props.put("hibernate.cache.infinispan.jgroups_cfg",jChannelConfig);
1✔
167
                        
168
                        // Load infinispan config based on selected cache type
169
                        String local = "local".equalsIgnoreCase(cacheType.trim()) ? "-local" : "";
1✔
170
                        props.put("hibernate.cache.infinispan.cfg", 
1✔
171
                                "org/infinispan/hibernate/cache/commons/builder/infinispan-configs" + local + ".xml");
172
                        
173
                        // Only load in the default properties if they don't exist
174
                        for (Entry<Object, Object> prop : props.entrySet()) {
1✔
175
                                if (!config.containsKey(prop.getKey())) {
1✔
176
                                        config.put(prop.getKey(), prop.getValue());
1✔
177
                                }
178
                        }
1✔
179
                        
180
                }
181
                catch (IOException e) {
×
182
                        log.error(MarkerFactory.getMarker("FATAL"), "Unable to load default hibernate properties", e);
×
183
                }
1✔
184
                
185
                log.debug("Replacing variables in hibernate properties");
1✔
186
                final String applicationDataDirectory = OpenmrsUtil.getApplicationDataDirectory();
1✔
187
                for (Entry<Object, Object> entry : config.entrySet()) {
1✔
188
                        String value = (String) entry.getValue();
1✔
189
                        
190
                        value = value.replace("%APPLICATION_DATA_DIRECTORY%", applicationDataDirectory);
1✔
191
                        entry.setValue(value);
1✔
192
                }
1✔
193

194
                log.debug("Setting global Hibernate Session Interceptor for SessionFactory, Interceptor: {}", chainingInterceptor);
1✔
195
                
196
                // make sure all autowired interceptors are put onto our chaining interceptor
197
                // sort on the keys so that the devs/modules have some sort of control over the order of the interceptors 
198
                List<String> keys = new ArrayList<>(interceptors.keySet());
1✔
199
                Collections.sort(keys);
1✔
200
                for (String key : keys) {
1✔
201
                        chainingInterceptor.addInterceptor(interceptors.get(key));
1✔
202
                }
1✔
203
                
204
                setEntityInterceptor(chainingInterceptor);
1✔
205
                
206
                //Adding each module's mapping file to the list of mapping resources
207
                setMappingResources(getModuleMappingResources().toArray(new String[0]));
1✔
208
                
209
                setPackagesToScan(getModulePackagesWithMappedClasses().toArray(new String[0]));
1✔
210
                
211
                setHibernateIntegrators(this);
1✔
212
                
213
                super.afterPropertiesSet();
1✔
214
        }
1✔
215
        
216
        /**
217
         * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#destroy()
218
         */
219
        @Override
220
        public void destroy() throws HibernateException {
221
                super.destroy();
×
222
        }
×
223

224
        @Override
225
        public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory,
226
                        SessionFactoryServiceRegistry serviceRegistry) {
227
                this.metadata = metadata;
1✔
228
                generateEnversAuditTables(metadata, serviceRegistry);
1✔
229
        }
1✔
230

231
        @Override
232
        public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
233
                
234
        }
×
235

236
        /**
237
         * @since 2.4
238
         */
239
        public Metadata getMetadata() {
240
                return metadata;
1✔
241
        }
242

243
        private void generateEnversAuditTables(Metadata metadata, ServiceRegistry serviceRegistry) {
244
                try {
245
                        Properties hibernateProperties = getHibernateProperties();
1✔
246
                        EnversAuditTableInitializer.initialize(metadata, hibernateProperties, serviceRegistry);
1✔
NEW
247
                } catch (Exception e) {
×
NEW
248
                        throw new APIException("An error occurred while initializing the Envers audit tables", e);
×
249
                }
1✔
250
        }
1✔
251
}
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