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

openmrs / openmrs-core / 18855934517

27 Oct 2025 09:07PM UTC coverage: 64.894%. Remained the same
18855934517

push

github

web-flow
TRUNK-6457 - Core DAO authenticate method fails to retrieve global properties (#5439)

7 of 15 new or added lines in 1 file covered. (46.67%)

3 existing lines in 2 files now uncovered.

23439 of 36119 relevant lines covered (64.89%)

0.65 hits per line

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

65.25
/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 org.apache.commons.lang3.StringUtils;
13
import org.hibernate.CacheMode;
14
import org.hibernate.FlushMode;
15
import org.hibernate.HibernateException;
16
import org.hibernate.ScrollableResults;
17
import org.hibernate.Session;
18
import org.hibernate.SessionFactory;
19
import org.hibernate.search.FullTextSession;
20
import org.hibernate.stat.QueryStatistics;
21
import org.hibernate.stat.Statistics;
22
import org.hibernate.type.StandardBasicTypes;
23
import org.openmrs.GlobalProperty;
24
import org.openmrs.OpenmrsObject;
25
import org.openmrs.User;
26
import org.openmrs.api.context.Context;
27
import org.openmrs.api.context.ContextAuthenticationException;
28
import org.openmrs.api.context.Daemon;
29
import org.openmrs.api.db.ContextDAO;
30
import org.openmrs.api.db.FullTextSessionFactory;
31
import org.openmrs.api.db.UserDAO;
32
import org.openmrs.util.OpenmrsConstants;
33
import org.openmrs.util.OpenmrsUtil;
34
import org.openmrs.util.PrivilegeConstants;
35
import org.openmrs.util.Security;
36
import org.slf4j.Logger;
37
import org.slf4j.LoggerFactory;
38
import org.springframework.beans.factory.annotation.Autowired;
39
import org.springframework.orm.hibernate5.SessionFactoryUtils;
40
import org.springframework.orm.hibernate5.SessionHolder;
41
import org.springframework.transaction.annotation.Transactional;
42
import org.springframework.transaction.support.TransactionSynchronizationManager;
43

44
import java.io.File;
45
import java.net.URL;
46
import java.sql.Connection;
47
import java.sql.SQLException;
48
import java.util.HashMap;
49
import java.util.List;
50
import java.util.Map;
51
import java.util.Properties;
52
import java.util.concurrent.Future;
53
import java.util.concurrent.TimeUnit;
54

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

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

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

102
                User candidateUser = null;
1✔
103

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

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

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

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

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

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

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

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

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

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

191
                                attempts++;
1✔
192

193
                                int allowedFailedLoginCount = 7;
1✔
194
                                try {
195
                                        Context.addProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
1✔
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
                                finally {
204
                                        Context.removeProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
1✔
205
                                }
206

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

215
                                saveUserProperties(candidateUser);
1✔
216
                        }
217
                }
218

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

386
        /**
387
         * @see org.openmrs.api.db.ContextDAO#evictEntity(OpenmrsObject)
388
         */
389
        @Override
390
        public void evictEntity(OpenmrsObject obj) {
391
                sessionFactory.getCache().evictEntity(obj.getClass(), obj.getId());
1✔
392
        }
1✔
393

394
        /**
395
         * @see org.openmrs.api.db.ContextDAO#evictAllEntities(Class)
396
         */
397
        @Override
398
        public void evictAllEntities(Class<?> entityClass) {
399
                sessionFactory.getCache().evictEntityRegion(entityClass);
1✔
400
                sessionFactory.getCache().evictCollectionRegions();
1✔
401
                sessionFactory.getCache().evictQueryRegions();
1✔
402
        }
1✔
403

404
        /**
405
         * @see org.openmrs.api.db.ContextDAO#clearEntireCache()
406
         */
407
        @Override
408
        public void clearEntireCache() {
409
                sessionFactory.getCache().evictAllRegions();
1✔
410
        }
1✔
411
        
412
        /**
413
         * @see org.openmrs.api.db.ContextDAO#refreshEntity(Object)
414
         */
415
        @Override
416
        public void refreshEntity(Object obj) {
417
                sessionFactory.getCurrentSession().refresh(obj);
1✔
418
        }
1✔
419

420
        /**
421
         * @see org.openmrs.api.db.ContextDAO#flushSession()
422
         */
423
        @Override
424
        @Transactional
425
        public void flushSession() {
426
                sessionFactory.getCurrentSession().flush();
1✔
427
        }
1✔
428
        
429
        /**
430
         * @see org.openmrs.api.context.Context#startup(Properties)
431
         */
432
        @Override
433
        @Transactional
434
        public void startup(Properties properties) {
435
        }
×
436
        
437
        /**
438
         * @see org.openmrs.api.context.Context#shutdown()
439
         */
440
        @Override
441
        public void shutdown() {
442
                if (log.isInfoEnabled()) {
×
443
                        showUsageStatistics();
×
444
                }
445
                
446
                if (sessionFactory != null) {
×
447
                        
448
                        log.debug("Closing any open sessions");
×
449
                        closeSession();
×
450
                        
451
                        log.debug("Shutting down threadLocalSession factory");
×
452
                        if (!sessionFactory.isClosed()) {
×
453
                                sessionFactory.close();
×
454
                        }
455
                        
456
                        log.debug("The threadLocalSession has been closed");
×
457
                        
458
                } else {
459
                        log.error("SessionFactory is null");
×
460
                }
461
                
462
        }
×
463
        
464
        /**
465
         * Convenience method to print out the hibernate cache usage stats to the log
466
         */
467
        private void showUsageStatistics() {
468
                if (sessionFactory.getStatistics().isStatisticsEnabled()) {
×
469
                        log.debug("Getting query statistics: ");
×
470
                        Statistics stats = sessionFactory.getStatistics();
×
471
                        for (String query : stats.getQueries()) {
×
472
                                log.info("QUERY: " + query);
×
473
                                QueryStatistics qstats = stats.getQueryStatistics(query);
×
474
                                log.info("Cache Hit Count : " + qstats.getCacheHitCount());
×
475
                                log.info("Cache Miss Count: " + qstats.getCacheMissCount());
×
476
                                log.info("Cache Put Count : " + qstats.getCachePutCount());
×
477
                                log.info("Execution Count : " + qstats.getExecutionCount());
×
478
                                log.info("Average time    : " + qstats.getExecutionAvgTime());
×
479
                                log.info("Row Count       : " + qstats.getExecutionRowCount());
×
480
                        }
481
                }
482
        }
×
483
        
484
        /**
485
         * Takes the default properties defined in /metadata/api/hibernate/hibernate.default.properties
486
         * and merges it into the user-defined runtime properties
487
         * 
488
         * @see org.openmrs.api.db.ContextDAO#mergeDefaultRuntimeProperties(java.util.Properties)
489
         * <strong>Should</strong> merge default runtime properties
490
         */
491
        @Override
492
        public void mergeDefaultRuntimeProperties(Properties runtimeProperties) {
493
                
494
                Map<String, String> cache = new HashMap<>();
1✔
495
                // loop over runtime properties and precede each with "hibernate" if
496
                // it isn't already
497
                for (Map.Entry<Object, Object> entry : runtimeProperties.entrySet()) {
1✔
498
                        Object key = entry.getKey();
1✔
499
                        String prop = (String) key;
1✔
500
                        String value = (String) entry.getValue();
1✔
501
                        log.trace("Setting property: " + prop + ":" + value);
1✔
502
                        if (!prop.startsWith("hibernate") && !runtimeProperties.containsKey("hibernate." + prop)) {
1✔
503
                                cache.put("hibernate." + prop, value);
1✔
504
                        }
505
                }
1✔
506
                runtimeProperties.putAll(cache);
1✔
507
                
508
                // load in the default hibernate properties from hibernate.default.properties
509
                Properties props = new Properties();
1✔
510
                URL url = getClass().getResource("/hibernate.default.properties");
1✔
511
                File file = new File(url.getPath());
1✔
512
                OpenmrsUtil.loadProperties(props, file);
1✔
513
                
514
                // add in all default properties that don't exist in the runtime
515
                // properties yet
516
                for (Map.Entry<Object, Object> entry : props.entrySet()) {
1✔
517
                        if (!runtimeProperties.containsKey(entry.getKey())) {
1✔
518
                                runtimeProperties.put(entry.getKey(), entry.getValue());
1✔
519
                        }
520
                }
1✔
521
        }
1✔
522
        
523
        @Override
524
        @Transactional
525
        public void updateSearchIndexForType(Class<?> type) {
526
                //From http://docs.jboss.org/hibernate/search/3.3/reference/en-US/html/manual-index-changes.html#search-batchindex-flushtoindexes
527
                FullTextSession session = fullTextSessionFactory.getFullTextSession();
1✔
528
                session.purgeAll(type);
1✔
529
                
530
                //Prepare session for batch work
531
                session.flush();
1✔
532
                session.clear();
1✔
533
                
534
                FlushMode flushMode = session.getHibernateFlushMode();
1✔
535
                CacheMode cacheMode = session.getCacheMode();
1✔
536
                try {
537
                        session.setHibernateFlushMode(FlushMode.MANUAL);
1✔
538
                        session.setCacheMode(CacheMode.IGNORE);
1✔
539
                        
540
                        //Scrollable results will avoid loading too many objects in memory
541
                        try (ScrollableResults results = HibernateUtil.getScrollableResult(sessionFactory, type, 1000)) {
1✔
542
                                int index = 0;
1✔
543
                                while (results.next()) {
1✔
544
                                        index++;
1✔
545
                                        //index each element
546
                                        session.index(results.get(0));
1✔
547
                                        if (index % 1000 == 0) {
1✔
548
                                                //apply changes to indexes
549
                                                session.flushToIndexes();
1✔
550
                                                //free memory since the queue is processed
551
                                                session.clear();
1✔
552
                                                // reset index to avoid overflows
553
                                                index = 0;
1✔
554
                                        }
555
                                }
556
                        } finally {
557
                                session.flushToIndexes();
1✔
558
                                session.clear();
1✔
559
                        }
560
                }
561
                finally {
562
                        session.setHibernateFlushMode(flushMode);
1✔
563
                        session.setCacheMode(cacheMode);
1✔
564
                }
565
        }
1✔
566
        
567
        /**
568
         * @see org.openmrs.api.db.ContextDAO#updateSearchIndexForObject(java.lang.Object)
569
         */
570
        @Override
571
        @Transactional
572
        public void updateSearchIndexForObject(Object object) {
573
                FullTextSession session = fullTextSessionFactory.getFullTextSession();
×
574
                session.index(object);
×
575
                session.flushToIndexes();
×
576
        }
×
577
        
578
        /**
579
         * @see org.openmrs.api.db.ContextDAO#setupSearchIndex()
580
         */
581
        @Override
582
        public void setupSearchIndex() {
583
                try {
NEW
584
                        Context.addProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
NEW
585
                        String gp = Context.getAdministrationService().getGlobalProperty(OpenmrsConstants.GP_SEARCH_INDEX_VERSION, "");
×
586

NEW
587
                        if (!OpenmrsConstants.SEARCH_INDEX_VERSION.toString().equals(gp)) {
×
NEW
588
                                updateSearchIndex();
×
589
                        }
590
                }
591
                finally {
NEW
592
                        Context.removeProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
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.");
×
603
                        fullTextSessionFactory.getFullTextSession().createIndexer().startAndWait();
×
NEW
604
                        Context.addProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
UNCOV
605
                        GlobalProperty gp = Context.getAdministrationService().getGlobalPropertyObject(
×
606
                            OpenmrsConstants.GP_SEARCH_INDEX_VERSION);
607
                        if (gp == null) {
×
608
                                gp = new GlobalProperty(OpenmrsConstants.GP_SEARCH_INDEX_VERSION);
×
609
                        }
610
                        gp.setPropertyValue(OpenmrsConstants.SEARCH_INDEX_VERSION.toString());
×
611
                        Context.getAdministrationService().saveGlobalProperty(gp);
×
612
                        log.info("Finished updating the search index");
×
613
                }
614
                catch (Exception e) {
×
615
                        throw new RuntimeException("Failed to update the search index", e);
×
616
                }
617
                finally {
NEW
618
                        Context.removeProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
×
619
                }
UNCOV
620
        }
×
621

622
        /**
623
         * @see ContextDAO#updateSearchIndexAsync()
624
         */
625
        @Override
626
        public Future<?> updateSearchIndexAsync() {
627
                try {
628
                        log.info("Started asynchronously updating the search index...");
×
629
                        return fullTextSessionFactory.getFullTextSession().createIndexer().start();
×
630
                }
631
                catch (Exception e) {
×
632
                        throw new RuntimeException("Failed to start asynchronous search index update", e);
×
633
                }
634
        }
635

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