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

openmrs / openmrs-core / 14752386992

30 Apr 2025 10:25AM UTC coverage: 64.96% (-0.1%) from 65.095%
14752386992

push

github

web-flow
TRUNK-6316 Upgrade Hibernate Search to 6.2.4 (#5005)

501 of 591 new or added lines in 24 files covered. (84.77%)

21 existing lines in 6 files now uncovered.

23344 of 35936 relevant lines covered (64.96%)

0.65 hits per line

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

64.29
/api/src/main/java/org/openmrs/api/db/hibernate/HibernateContextDAO.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.File;
13
import java.net.URL;
14
import java.sql.Connection;
15
import java.sql.SQLException;
16
import java.util.HashMap;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Properties;
20
import java.util.concurrent.Future;
21
import java.util.concurrent.TimeUnit;
22

23
import org.apache.commons.lang3.StringUtils;
24
import org.hibernate.CacheMode;
25
import org.hibernate.FlushMode;
26
import org.hibernate.HibernateException;
27
import org.hibernate.ScrollableResults;
28
import org.hibernate.Session;
29
import org.hibernate.SessionFactory;
30
import org.hibernate.search.mapper.orm.Search;
31
import org.hibernate.search.mapper.orm.session.SearchSession;
32
import org.hibernate.search.mapper.orm.work.SearchIndexingPlan;
33
import org.hibernate.stat.QueryStatistics;
34
import org.hibernate.stat.Statistics;
35
import org.hibernate.type.StandardBasicTypes;
36
import org.openmrs.GlobalProperty;
37
import org.openmrs.OpenmrsObject;
38
import org.openmrs.User;
39
import org.openmrs.api.context.Context;
40
import org.openmrs.api.context.ContextAuthenticationException;
41
import org.openmrs.api.context.Daemon;
42
import org.openmrs.api.db.ContextDAO;
43
import org.openmrs.api.db.UserDAO;
44
import org.openmrs.api.db.hibernate.search.session.SearchSessionFactory;
45
import org.openmrs.util.OpenmrsConstants;
46
import org.openmrs.util.OpenmrsUtil;
47
import org.openmrs.util.Security;
48
import org.slf4j.Logger;
49
import org.slf4j.LoggerFactory;
50
import org.springframework.beans.factory.annotation.Autowired;
51
import org.springframework.orm.hibernate5.SessionFactoryUtils;
52
import org.springframework.orm.hibernate5.SessionHolder;
53
import org.springframework.transaction.annotation.Transactional;
54
import org.springframework.transaction.support.TransactionSynchronizationManager;
55

56
/**
57
 * Hibernate specific implementation of the {@link ContextDAO}. These methods should not be used
58
 * directly, instead, the methods on the static {@link Context} file should be used.
59
 * 
60
 * @see ContextDAO
61
 * @see Context
62
 */
63
public class HibernateContextDAO implements ContextDAO {
1✔
64
        
65
        private static final Logger log = LoggerFactory.getLogger(HibernateContextDAO.class);
1✔
66
        
67
        private static final Long DEFAULT_UNLOCK_ACCOUNT_WAITING_TIME = TimeUnit.MILLISECONDS.convert(5L, TimeUnit.MINUTES);
1✔
68
        
69
        /**
70
         * Hibernate session factory
71
         */
72
        private SessionFactory sessionFactory;
73
        
74
        @Autowired
75
        private SearchSessionFactory searchSessionFactory;
76
        
77
        private UserDAO userDao;
78
        
79
        /**
80
         * Session factory to use for this DAO. This is usually injected by spring and its application
81
         * context.
82
         * 
83
         * @param sessionFactory
84
         */
85
        public void setSessionFactory(SessionFactory sessionFactory) {
86
                this.sessionFactory = sessionFactory;
1✔
87
        }
1✔
88
        
89
        public void setUserDAO(UserDAO userDao) {
90
                this.userDao = userDao;
1✔
91
        }
1✔
92

93
        /**
94
         * @see org.openmrs.api.db.ContextDAO#authenticate(java.lang.String, java.lang.String)
95
         */
96
        @Override
97
        @Transactional(noRollbackFor = ContextAuthenticationException.class)
98
        public User authenticate(String login, String password) throws ContextAuthenticationException {
99
                String errorMsg = "Invalid username and/or password: " + login;
1✔
100

101
                Session session = sessionFactory.getCurrentSession();
1✔
102

103
                User candidateUser = null;
1✔
104

105
                if (StringUtils.isNotBlank(login)) {
1✔
106
                        // loginWithoutDash is used to compare to the system id
107
                        String loginWithDash = login;
1✔
108
                        if (login.matches("\\d{2,}")) {
1✔
109
                                loginWithDash = login.substring(0, login.length() - 1) + "-" + login.charAt(login.length() - 1);
1✔
110
                        }
111

112
                        try {
113
                                candidateUser = session.createQuery(
1✔
114
                                        "from User u where (u.username = ?1 or u.systemId = ?2 or u.systemId = ?3) and u.retired = false",
115
                                        User.class)
116
                                        .setParameter(1, login).setParameter(2, login).setParameter(3, loginWithDash).uniqueResult();
1✔
117
                        }
118
                        catch (HibernateException he) {
×
119
                                log.error("Got hibernate exception while logging in: '{}'", login, he);
×
120
                        }
121
                        catch (Exception e) {
×
122
                                log.error("Got regular exception while logging in: '{}'", login, e);
×
123
                        }
1✔
124
                }
125

126
                // only continue if this is a valid username and a nonempty password
127
                if (candidateUser != null && password != null) {
1✔
128
                        log.debug("Candidate user id: {}", candidateUser.getUserId());
1✔
129

130
                        String lockoutTimeString = candidateUser.getUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP, null);
1✔
131
                        long lockoutTime = -1;
1✔
132
                        if (StringUtils.isNotBlank(lockoutTimeString) && !"0".equals(lockoutTimeString)) {
1✔
133
                                try {
134
                                        // putting this in a try/catch in case the admin decided to put junk into the property
135
                                        lockoutTime = Long.parseLong(lockoutTimeString);
1✔
136
                                }
137
                                catch (NumberFormatException e) {
×
138
                                        log.warn("bad value stored in {} user property: {}", OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP,
×
139
                                                lockoutTimeString);
140
                                }
1✔
141
                        }
142

143
                        // if they've been locked out, don't continue with the authentication
144
                        if (lockoutTime > 0) {
1✔
145
                                // unlock them after x mins, otherwise reset the timestamp
146
                                // to now and make them wait another x mins
147
                                final Long unlockTime = getUnlockTimeMs();
1✔
148
                                if (System.currentTimeMillis() - lockoutTime > unlockTime) {
1✔
149
                                        candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, OpenmrsConstants.ZERO_LOGIN_ATTEMPTS_VALUE);
×
150
                                        candidateUser.removeUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP);
×
151
                                        saveUserProperties(candidateUser);
×
152
                                } else {
153
                                        candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP, String.valueOf(System
1✔
154
                                                .currentTimeMillis()));
1✔
155
                                        throw new ContextAuthenticationException(
1✔
156
                                                "Invalid number of connection attempts. Please try again later.");
157
                                }
158
                        }
159

160
                        Object[] passwordAndSalt = (Object[]) session
1✔
161
                                .createNativeQuery("select password, salt from users where user_id = ?1")
1✔
162
                                .addScalar("password", StandardBasicTypes.STRING).addScalar("salt", StandardBasicTypes.STRING)
1✔
163
                                .setParameter(1, candidateUser.getUserId()).uniqueResult();
1✔
164

165
                        String passwordOnRecord = (String) passwordAndSalt[0];
1✔
166
                        String saltOnRecord = (String) passwordAndSalt[1];
1✔
167

168
                        // if the username and password match, hydrate the user and return it
169
                        if (passwordOnRecord != null && Security.hashMatches(passwordOnRecord, password + saltOnRecord)) {
1✔
170
                                // hydrate the user object
171
                                candidateUser.getAllRoles().size();
1✔
172
                                candidateUser.getUserProperties().size();
1✔
173
                                candidateUser.getPrivileges().size();
1✔
174

175
                                // only clean up if the were some login failures, otherwise all should be clean
176
                                int attempts = getUsersLoginAttempts(candidateUser);
1✔
177
                                if (attempts > 0) {
1✔
178
                                        candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, OpenmrsConstants.ZERO_LOGIN_ATTEMPTS_VALUE);
1✔
179
                                        candidateUser.removeUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP);
1✔
180
                                }
181
                                setLastLoginTime(candidateUser);
1✔
182
                                saveUserProperties(candidateUser);
1✔
183

184
                                // skip out of the method early (instead of throwing the exception)
185
                                // to indicate that this is the valid user
186
                                return candidateUser;
1✔
187
                        } else {
188
                                // the user failed the username/password, increment their
189
                                // attempts here and set the "lockout" timestamp if necessary
190
                                int attempts = getUsersLoginAttempts(candidateUser);
1✔
191

192
                                attempts++;
1✔
193

194
                                int allowedFailedLoginCount = 7;
1✔
195
                                try {
196
                                        allowedFailedLoginCount = Integer.parseInt(Context.getAdministrationService().getGlobalProperty(
1✔
197
                                                OpenmrsConstants.GP_ALLOWED_FAILED_LOGINS_BEFORE_LOCKOUT).trim());
×
198
                                }
199
                                catch (Exception ex) {
1✔
200
                                        log.error("Unable to convert the global property {} to a valid integer. Using default value of 7.",
1✔
201
                                                OpenmrsConstants.GP_ALLOWED_FAILED_LOGINS_BEFORE_LOCKOUT);
202
                                }
×
203

204
                                if (attempts > allowedFailedLoginCount) {
1✔
205
                                        // set the user as locked out at this exact time
206
                                        candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP, String.valueOf(System
1✔
207
                                                .currentTimeMillis()));
1✔
208
                                } else {
209
                                        candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, String.valueOf(attempts));
1✔
210
                                }
211

212
                                saveUserProperties(candidateUser);
1✔
213
                        }
214
                }
215

216
                // throw this exception only once in the same place with the same
217
                // message regardless of username/pw combo entered
218
                log.info("Failed login attempt (login={}) - {}", login, errorMsg);
1✔
219
                throw new ContextAuthenticationException(errorMsg);
1✔
220
        }
221
        
222
        private void setLastLoginTime(User candidateUser) {
223
                candidateUser.setUserProperty(
1✔
224
                        OpenmrsConstants.USER_PROPERTY_LAST_LOGIN_TIMESTAMP,
225
                        String.valueOf(System.currentTimeMillis())
1✔
226
                );
227
        }
1✔
228
        
229
        private Long getUnlockTimeMs() {
230
                String unlockTimeGPValue = Context.getAdministrationService().getGlobalProperty(
1✔
231
                                OpenmrsConstants.GP_UNLOCK_ACCOUNT_WAITING_TIME);
232
                if (StringUtils.isNotBlank(unlockTimeGPValue)) {
1✔
233
                        return convertUnlockAccountWaitingTimeGP(unlockTimeGPValue);
×
234
                }
235
                else {
236
                        return DEFAULT_UNLOCK_ACCOUNT_WAITING_TIME;
1✔
237
                }
238
        }
239
        
240
        private Long convertUnlockAccountWaitingTimeGP(String waitingTime) {
241
                try {
242
                        return TimeUnit.MILLISECONDS.convert(Long.valueOf(waitingTime), TimeUnit.MINUTES);
×
243
                } catch (Exception ex) {
×
244
                        log.error("Unable to convert the global property "
×
245
                                        + OpenmrsConstants.GP_UNLOCK_ACCOUNT_WAITING_TIME
246
                                        + "to a valid Long. Using default value of 5");
247
                        return DEFAULT_UNLOCK_ACCOUNT_WAITING_TIME;
×
248
                }
249
        }
250
        
251
        /**
252
         * @see org.openmrs.api.db.ContextDAO#getUserByUuid(java.lang.String)
253
         */
254
        @Override
255
        @Transactional(readOnly = true)
256
        public User getUserByUuid(String uuid) {
257
                
258
                // don't flush here in case we're in the AuditableInterceptor.  Will cause a StackOverflowEx otherwise
259
                FlushMode flushMode = sessionFactory.getCurrentSession().getHibernateFlushMode();
1✔
260
                sessionFactory.getCurrentSession().setHibernateFlushMode(FlushMode.MANUAL);
1✔
261
                
262
                User u = HibernateUtil.getUniqueEntityByUUID(sessionFactory, User.class, uuid);
1✔
263
                
264
                // reset the flush mode to whatever it was before
265
                sessionFactory.getCurrentSession().setHibernateFlushMode(flushMode);
1✔
266
                
267
                return u;
1✔
268
        }
269
        
270
        /**
271
         * @see org.openmrs.api.db.ContextDAO#getUserByUsername(String)
272
         */
273
        @Override
274
        @Transactional(readOnly = true)
275
        public User getUserByUsername(String username) {
276
                return userDao.getUserByUsername(username);
×
277
        }
278
        
279
        /**
280
         * @throws Exception 
281
         * @see org.openmrs.api.db.ContextDAO#createUser(User, String)
282
         */
283
        @Override
284
        @Transactional
285
        public User createUser(User user, String password, List<String> roleNames) throws Exception {
286
                return Daemon.createUser(user, password, roleNames);
1✔
287
        }
288
        
289
        /**
290
         * Call the UserService to save the given user while proxying the privileges needed to do so.
291
         * 
292
         * @param user the User to save
293
         */
294
        private void saveUserProperties(User user) {
295
                sessionFactory.getCurrentSession().update(user);
1✔
296
        }
1✔
297
        
298
        /**
299
         * Get the integer stored for the given user that is their number of login attempts
300
         * 
301
         * @param user the user to check
302
         * @return the # of login attempts for this user defaulting to zero if none defined
303
         */
304
        private int getUsersLoginAttempts(User user) {
305
                String attemptsString = user.getUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, "0");
1✔
306
                int attempts = 0;
1✔
307
                try {
308
                        attempts = Integer.parseInt(attemptsString);
1✔
309
                }
310
                catch (NumberFormatException e) {
×
311
                        // skip over errors and leave the attempts at zero
312
                }
1✔
313
                return attempts;
1✔
314
        }
315
        
316
        /**
317
         * @see org.openmrs.api.context.Context#openSession()
318
         */
319
        private boolean participate = false;
1✔
320
        
321
        @Override
322
        public void openSession() {
323
                log.debug("HibernateContext: Opening Hibernate Session");
1✔
324
                if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
1✔
325
                        log.debug("Participating in existing session ({})", sessionFactory.hashCode());
1✔
326
                        participate = true;
1✔
327
                } else {
328
                        log.debug("Registering session with synchronization manager ({})", sessionFactory.hashCode());
1✔
329
                        Session session = sessionFactory.openSession();
1✔
330
                        session.setHibernateFlushMode(FlushMode.MANUAL);
1✔
331
                        TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
1✔
332
                }
333
        }
1✔
334
        
335
        /**
336
         * @see org.openmrs.api.context.Context#closeSession()
337
         */
338
        @Override
339
        public void closeSession() {
340
                log.debug("HibernateContext: closing Hibernate Session");
1✔
341
                if (!participate) {
1✔
342
                        log.debug("Unbinding session from synchronization manager (" + sessionFactory.hashCode() + ")");
×
343
                        
344
                        if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
×
345
                                Object value = TransactionSynchronizationManager.unbindResource(sessionFactory);
×
346
                                try {
347
                                        if (value instanceof SessionHolder) {
×
348
                                                Session session = ((SessionHolder) value).getSession();
×
349
                                                SessionFactoryUtils.closeSession(session);
×
350
                                        }
351
                                }
352
                                catch (RuntimeException e) {
×
353
                                        log.error("Unexpected exception on closing Hibernate Session", e);
×
354
                                }
×
355
                        }
×
356
                } else {
357
                        log.debug("Participating in existing session, so not releasing session through synchronization manager");
1✔
358
                }
359
        }
1✔
360
        
361
        /**
362
         * @see org.openmrs.api.db.ContextDAO#clearSession()
363
         */
364
        @Override
365
        @Transactional
366
        public void clearSession() {
367
                sessionFactory.getCurrentSession().clear();
1✔
368
        }
1✔
369
        
370
        /**
371
         * @see org.openmrs.api.db.ContextDAO#evictFromSession(java.lang.Object)
372
         */
373
        @Override
374
        public void evictFromSession(Object obj) {
375
                sessionFactory.getCurrentSession().evict(obj);
1✔
376
        }
1✔
377

378
        /**
379
         * @see org.openmrs.api.db.ContextDAO#evictEntity(OpenmrsObject)
380
         */
381
        @Override
382
        public void evictEntity(OpenmrsObject obj) {
383
                sessionFactory.getCache().evictEntity(obj.getClass(), obj.getId());
1✔
384
        }
1✔
385

386
        /**
387
         * @see org.openmrs.api.db.ContextDAO#evictAllEntities(Class)
388
         */
389
        @Override
390
        public void evictAllEntities(Class<?> entityClass) {
391
                sessionFactory.getCache().evictEntityRegion(entityClass);
1✔
392
                sessionFactory.getCache().evictCollectionRegions();
1✔
393
                sessionFactory.getCache().evictQueryRegions();
1✔
394
        }
1✔
395

396
        /**
397
         * @see org.openmrs.api.db.ContextDAO#clearEntireCache()
398
         */
399
        @Override
400
        public void clearEntireCache() {
401
                sessionFactory.getCache().evictAllRegions();
1✔
402
        }
1✔
403
        
404
        /**
405
         * @see org.openmrs.api.db.ContextDAO#refreshEntity(Object)
406
         */
407
        @Override
408
        public void refreshEntity(Object obj) {
409
                sessionFactory.getCurrentSession().refresh(obj);
1✔
410
        }
1✔
411

412
        /**
413
         * @see org.openmrs.api.db.ContextDAO#flushSession()
414
         */
415
        @Override
416
        @Transactional
417
        public void flushSession() {
418
                sessionFactory.getCurrentSession().flush();
1✔
419
        }
1✔
420
        
421
        /**
422
         * @see org.openmrs.api.context.Context#startup(Properties)
423
         */
424
        @Override
425
        @Transactional
426
        public void startup(Properties properties) {
427
        }
×
428
        
429
        /**
430
         * @see org.openmrs.api.context.Context#shutdown()
431
         */
432
        @Override
433
        public void shutdown() {
434
                if (log.isInfoEnabled()) {
×
435
                        showUsageStatistics();
×
436
                }
437
                
438
                if (sessionFactory != null) {
×
439
                        
440
                        log.debug("Closing any open sessions");
×
441
                        closeSession();
×
442
                        
443
                        log.debug("Shutting down threadLocalSession factory");
×
444
                        if (!sessionFactory.isClosed()) {
×
445
                                sessionFactory.close();
×
446
                        }
447
                        
448
                        log.debug("The threadLocalSession has been closed");
×
449
                        
450
                } else {
451
                        log.error("SessionFactory is null");
×
452
                }
453
                
454
        }
×
455
        
456
        /**
457
         * Convenience method to print out the hibernate cache usage stats to the log
458
         */
459
        private void showUsageStatistics() {
460
                if (sessionFactory.getStatistics().isStatisticsEnabled()) {
×
461
                        log.debug("Getting query statistics: ");
×
462
                        Statistics stats = sessionFactory.getStatistics();
×
463
                        for (String query : stats.getQueries()) {
×
464
                                log.info("QUERY: " + query);
×
465
                                QueryStatistics qstats = stats.getQueryStatistics(query);
×
466
                                log.info("Cache Hit Count : " + qstats.getCacheHitCount());
×
467
                                log.info("Cache Miss Count: " + qstats.getCacheMissCount());
×
468
                                log.info("Cache Put Count : " + qstats.getCachePutCount());
×
469
                                log.info("Execution Count : " + qstats.getExecutionCount());
×
470
                                log.info("Average time    : " + qstats.getExecutionAvgTime());
×
471
                                log.info("Row Count       : " + qstats.getExecutionRowCount());
×
472
                        }
473
                }
474
        }
×
475
        
476
        /**
477
         * Takes the default properties defined in /metadata/api/hibernate/hibernate.default.properties
478
         * and merges it into the user-defined runtime properties
479
         * 
480
         * @see org.openmrs.api.db.ContextDAO#mergeDefaultRuntimeProperties(java.util.Properties)
481
         * <strong>Should</strong> merge default runtime properties
482
         */
483
        @Override
484
        public void mergeDefaultRuntimeProperties(Properties runtimeProperties) {
485
                
486
                Map<String, String> cache = new HashMap<>();
1✔
487
                // loop over runtime properties and precede each with "hibernate" if
488
                // it isn't already
489
                for (Map.Entry<Object, Object> entry : runtimeProperties.entrySet()) {
1✔
490
                        Object key = entry.getKey();
1✔
491
                        String prop = (String) key;
1✔
492
                        String value = (String) entry.getValue();
1✔
493
                        log.trace("Setting property: " + prop + ":" + value);
1✔
494
                        if (!prop.startsWith("hibernate") && !runtimeProperties.containsKey("hibernate." + prop)) {
1✔
495
                                cache.put("hibernate." + prop, value);
1✔
496
                        }
497
                }
1✔
498
                runtimeProperties.putAll(cache);
1✔
499
                
500
                // load in the default hibernate properties from hibernate.default.properties
501
                Properties props = new Properties();
1✔
502
                URL url = getClass().getResource("/hibernate.default.properties");
1✔
503
                File file = new File(url.getPath());
1✔
504
                OpenmrsUtil.loadProperties(props, file);
1✔
505
                
506
                // add in all default properties that don't exist in the runtime
507
                // properties yet
508
                for (Map.Entry<Object, Object> entry : props.entrySet()) {
1✔
509
                        if (!runtimeProperties.containsKey(entry.getKey())) {
1✔
510
                                runtimeProperties.put(entry.getKey(), entry.getValue());
1✔
511
                        }
512
                }
1✔
513
        }
1✔
514
        
515
        @Override
516
        @Transactional
517
        public void updateSearchIndexForType(Class<?> type) {
518
                Session session = sessionFactory.getCurrentSession();
1✔
519
                SearchSession searchSession = searchSessionFactory.getSearchSession();
1✔
520
                SearchIndexingPlan indexingPlan = searchSession.indexingPlan();
1✔
521
                
522
                //Prepare session for batch work
523
                session.flush();
1✔
524
                indexingPlan.execute();
1✔
525
                session.clear();
1✔
526

527
                //Purge all search indexes of the given type
528
                Search.mapping(sessionFactory).scope(type).workspace().purge();
1✔
529
                
530
                FlushMode flushMode = session.getHibernateFlushMode();
1✔
531
                CacheMode cacheMode = session.getCacheMode();
1✔
532
                try {
533
                        session.setHibernateFlushMode(FlushMode.MANUAL);
1✔
534
                        session.setCacheMode(CacheMode.IGNORE);
1✔
535

536
                        //Scrollable results will avoid loading too many objects in memory
537
                        try (ScrollableResults results = HibernateUtil.getScrollableResult(sessionFactory, type, 1000)) {
1✔
538
                                int index = 0;
1✔
539
                                while (results.next()) {
1✔
540
                                        index++;
1✔
541
                                        //index each element
542
                                        indexingPlan.addOrUpdate(results.get(0));
1✔
543
                                        if (index % 1000 == 0) {
1✔
544
                                                //apply changes to search indexes
545
                                                indexingPlan.execute();
1✔
546
                                                //free memory since the queue is processed
547
                                                session.clear();
1✔
548
                                                // reset index to avoid overflows
549
                                                index = 0;
1✔
550
                                        }
551
                                }
552
                        } finally {
553
                                indexingPlan.execute();
1✔
554
                                session.clear();
1✔
555
                        }
556
                }
557
                finally {
558
                        session.setHibernateFlushMode(flushMode);
1✔
559
                        session.setCacheMode(cacheMode);
1✔
560
                }
561
        }
1✔
562

563
        @Override
564
        @Transactional
565
        public void updateSearchIndex(Class<?>... types) {
566
                try {
NEW
567
                        searchSessionFactory.getSearchSession().massIndexer(types).startAndWait();
×
NEW
568
                } catch (InterruptedException e) {
×
NEW
569
                        throw new RuntimeException(e);
×
NEW
570
                }
×
NEW
571
        }
×
572

573
        /**
574
         * @see org.openmrs.api.db.ContextDAO#updateSearchIndexForObject(java.lang.Object)
575
         */
576
        @Override
577
        @Transactional
578
        public void updateSearchIndexForObject(Object object) {
NEW
579
                SearchIndexingPlan indexingPlan = searchSessionFactory.getSearchSession().indexingPlan();
×
NEW
580
                indexingPlan.addOrUpdate(object);
×
NEW
581
                indexingPlan.execute();
×
UNCOV
582
        }
×
583
        
584
        /**
585
         * @see org.openmrs.api.db.ContextDAO#setupSearchIndex()
586
         */
587
        @Override
588
        public void setupSearchIndex() {
589
                String gp = Context.getAdministrationService().getGlobalProperty(OpenmrsConstants.GP_SEARCH_INDEX_VERSION, "");
×
590
                
591
                if (!OpenmrsConstants.SEARCH_INDEX_VERSION.toString().equals(gp)) {
×
592
                        updateSearchIndex();
×
593
                }
594
        }
×
595
        
596
        /**
597
         * @see ContextDAO#updateSearchIndex()
598
         */
599
        @Override
600
        public void updateSearchIndex() {
601
                try {
602
                        log.info("Updating the search index... It may take a few minutes.");
×
NEW
603
                        searchSessionFactory.getSearchSession().massIndexer().startAndWait();
×
604
                        GlobalProperty gp = Context.getAdministrationService().getGlobalPropertyObject(
×
605
                            OpenmrsConstants.GP_SEARCH_INDEX_VERSION);
606
                        if (gp == null) {
×
607
                                gp = new GlobalProperty(OpenmrsConstants.GP_SEARCH_INDEX_VERSION);
×
608
                        }
609
                        gp.setPropertyValue(OpenmrsConstants.SEARCH_INDEX_VERSION.toString());
×
610
                        Context.getAdministrationService().saveGlobalProperty(gp);
×
611
                        log.info("Finished updating the search index");
×
612
                }
613
                catch (Exception e) {
×
614
                        throw new RuntimeException("Failed to update the search index", e);
×
615
                }
×
616
        }
×
617

618
        /**
619
         * @see ContextDAO#updateSearchIndexAsync()
620
         */
621
        @Override
622
        public Future<?> updateSearchIndexAsync() {
623
                try {
624
                        log.info("Started asynchronously updating the search index...");
×
NEW
625
                        return searchSessionFactory.getSearchSession().massIndexer().start().toCompletableFuture();
×
626
                }
627
                catch (Exception e) {
×
628
                        throw new RuntimeException("Failed to start asynchronous search index update", e);
×
629
                }
630
        }
631

632
        /**
633
         * @see ContextDAO#getDatabaseConnection() 
634
         */
635
        public Connection getDatabaseConnection() {
636
                try {
637
                        return SessionFactoryUtils.getDataSource(sessionFactory).getConnection();
×
638
                }
639
                catch (SQLException e) {
×
640
                        throw new RuntimeException("Unable to retrieve a database connection", e);
×
641
                }
642
        }
643
}
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