gt: 		catch (NumberFormatException e) {
context: /**
   * This Source Code Form is subject to the terms of the Mozilla Public License,
   * v. 2.0. If a copy of the MPL was not distributed with this file, You can
   * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
   * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
   *
   * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
   * graphic logo is a trademark of OpenMRS Inc.
   */
  package org.openmrs.api.db.hibernate;
  
  import java.io.File;
  import java.net.URL;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Properties;
  import java.util.concurrent.Future;
  
  import org.apache.commons.lang.StringUtils;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.hibernate.CacheMode;
  import org.hibernate.FlushMode;
  import org.hibernate.HibernateException;
  import org.hibernate.ScrollMode;
  import org.hibernate.ScrollableResults;
  import org.hibernate.Session;
  import org.hibernate.SessionFactory;
  import org.hibernate.search.FullTextSession;
  import org.hibernate.search.Search;
  import org.hibernate.stat.QueryStatistics;
  import org.hibernate.stat.Statistics;
  import org.hibernate.type.StandardBasicTypes;
  import org.openmrs.GlobalProperty;
  import org.openmrs.User;
  import org.openmrs.api.context.Context;
  import org.openmrs.api.context.ContextAuthenticationException;
  import org.openmrs.api.db.ContextDAO;
  import org.openmrs.util.OpenmrsConstants;
  import org.openmrs.util.OpenmrsUtil;
  import org.openmrs.util.Security;
  import org.springframework.orm.hibernate4.SessionFactoryUtils;
  import org.springframework.orm.hibernate4.SessionHolder;
  import org.springframework.transaction.annotation.Transactional;
  import org.springframework.transaction.support.TransactionSynchronizationManager;
  
  /**
   * Hibernate specific implementation of the {@link ContextDAO}. These methods should not be used
   * directly, instead, the methods on the static {@link Context} file should be used.
   * 
   * @see ContextDAO
   * @see Context
   */
  public class HibernateContextDAO implements ContextDAO {
  	
  	private static Log log = LogFactory.getLog(HibernateContextDAO.class);
  	
  	/**
  	 * Hibernate session factory
  	 */
  	private SessionFactory sessionFactory;
  	
  	/**
  	 * Session factory to use for this DAO. This is usually injected by spring and its application
  	 * context.
  	 * 
  	 * @param sessionFactory
  	 */
  	public void setSessionFactory(SessionFactory sessionFactory) {
  		this.sessionFactory = sessionFactory;
  	}
  	
  	/**
  	 * @see org.openmrs.api.db.ContextDAO#authenticate(java.lang.String, java.lang.String)
  	 */
  	@Transactional(noRollbackFor = ContextAuthenticationException.class)
  	public User authenticate(String login, String password) throws ContextAuthenticationException {
  		
  		String errorMsg = "Invalid username and/or password: " + login;
  		
  		Session session = sessionFactory.getCurrentSession();
  		
  		User candidateUser = null;
  		
  		if (login != null) {
  			//if username is blank or white space character(s)
  			if (StringUtils.isEmpty(login) || StringUtils.isWhitespace(login)) {
  				throw new ContextAuthenticationException(errorMsg);
  			}
  			
  			// loginWithoutDash is used to compare to the system id
  			String loginWithDash = login;
  			if (login.matches("\\d{2,}")) {
  				loginWithDash = login.substring(0, login.length() - 1) + "-" + login.charAt(login.length() - 1);
  			}
  			
  			try {
  				candidateUser = (User) session.createQuery(
  				    "from User u where (u.username = ? or u.systemId = ? or u.systemId = ?) and u.retired = '0'").setString(
  				    0, login).setString(1, login).setString(2, loginWithDash).uniqueResult();
  			}
  			catch (HibernateException he) {
  				log.error("Got hibernate exception while logging in: '" + login + "'", he);
  			}
  			catch (Exception e) {
  				log.error("Got regular exception while logging in: '" + login + "'", e);
  			}
  		}
  		
  		// only continue if this is a valid username and a nonempty password
  		if (candidateUser != null && password != null) {
  			if (log.isDebugEnabled()) {
  				log.debug("Candidate user id: " + candidateUser.getUserId());
  			}
  			
  			String lockoutTimeString = candidateUser.getUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP, null);
  			Long lockoutTime = null;
  			if (lockoutTimeString != null && !lockoutTimeString.equals("0")) {
  				try {
  					// putting this in a try/catch in case the admin decided to put junk into the property
  					lockoutTime = Long.valueOf(lockoutTimeString);
  				}
  				catch (NumberFormatException e) {
  					log.debug("bad value stored in " + OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP + " user property: "
  					        + lockoutTimeString);
  				}
  			}
  			
  			// if they've been locked out, don't continue with the authentication
  			if (lockoutTime != null) {
  				// unlock them after 5 mins, otherwise reset the timestamp
  				// to now and make them wait another 5 mins
  				if (System.currentTimeMillis() - lockoutTime > 300000) {
  					candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, "0");
  					candidateUser.removeUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP);
  					saveUserProperties(candidateUser);
  				} else {
  					candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP, String.valueOf(System
  					        .currentTimeMillis()));
  					throw new ContextAuthenticationException(
  					        "Invalid number of connection attempts. Please try again later.");
  				}
  			}
  			
  			String passwordOnRecord = (String) session.createSQLQuery("select password from users where user_id = ?")
  			        .addScalar("password", StandardBasicTypes.STRING).setInteger(0, candidateUser.getUserId())
  			        .uniqueResult();
  			
  			String saltOnRecord = (String) session.createSQLQuery("select salt from users where user_id = ?").addScalar(
  			    "salt", StandardBasicTypes.STRING).setInteger(0, candidateUser.getUserId()).uniqueResult();
  			
  			// if the username and password match, hydrate the user and return it
  			if (passwordOnRecord != null && Security.hashMatches(passwordOnRecord, password + saltOnRecord)) {
  				// hydrate the user object
  				candidateUser.getAllRoles().size();
  				candidateUser.getUserProperties().size();
  				candidateUser.getPrivileges().size();
  				
  				// only clean up if the were some login failures, otherwise all should be clean
  				Integer attempts = getUsersLoginAttempts(candidateUser);
  				if (attempts > 0) {
  					candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, "0");
  					candidateUser.removeUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP);
  					saveUserProperties(candidateUser);
  				}
  				
  				// skip out of the method early (instead of throwing the exception)
  				// to indicate that this is the valid user
  				return candidateUser;
  			} else {
  				// the user failed the username/password, increment their
  				// attempts here and set the "lockout" timestamp if necessary
  				Integer attempts = getUsersLoginAttempts(candidateUser);
  				
  				attempts++;
  				
  				Integer allowedFailedLoginCount = 7;
  				
  				try {
  					allowedFailedLoginCount = Integer.valueOf(Context.getAdministrationService().getGlobalProperty(
  					    OpenmrsConstants.GP_ALLOWED_FAILED_LOGINS_BEFORE_LOCKOUT).trim());
  				}
  				catch (Exception ex) {
  					log.error("Unable to convert the global property "
  					        + OpenmrsConstants.GP_ALLOWED_FAILED_LOGINS_BEFORE_LOCKOUT
  					        + "to a valid integer. Using default value of 7");
  				}
  				
  				if (attempts > allowedFailedLoginCount) {
  					// set the user as locked out at this exact time
  					candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP, String.valueOf(System
  					        .currentTimeMillis()));
  				} else {
  					candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, String.valueOf(attempts));
  				}
  				
  				saveUserProperties(candidateUser);
  			}
  		}
  		
  		// throw this exception only once in the same place with the same
  		// message regardless of username/pw combo entered
  		log.info("Failed login attempt (login=" + login + ") - " + errorMsg);
  		throw new ContextAuthenticationException(errorMsg);
  		
  	}
  	
  	/**
  	 * @see org.openmrs.api.db.ContextDAO#getUserByUuid(java.lang.String)
  	 */
  	@Transactional(readOnly = true)
  	public User getUserByUuid(String uuid) {
  		
  		// don't flush here in case we're in the AuditableInterceptor.  Will cause a StackOverflowEx otherwise
  		FlushMode flushMode = sessionFactory.getCurrentSession().getFlushMode();
  		sessionFactory.getCurrentSession().setFlushMode(FlushMode.MANUAL);
  		
  		User u = (User) sessionFactory.getCurrentSession().createQuery("from User u where u.uuid = :uuid").setString("uuid",
  		    uuid).uniqueResult();
  		
  		// reset the flush mode to whatever it was before
  		sessionFactory.getCurrentSession().setFlushMode(flushMode);
  		
  		return u;
  	}
  	
  	/**
  	 * Call the UserService to save the given user while proxying the privileges needed to do so.
  	 * 
  	 * @param user the User to save
  	 */
  	private void saveUserProperties(User user) {
  		sessionFactory.getCurrentSession().update(user);
  	}
  	
  	/**
  	 * Get the integer stored for the given user that is their number of login attempts
  	 * 
  	 * @param user the user to check
  	 * @return the # of login attempts for this user defaulting to zero if none defined
  	 */
  	private Integer getUsersLoginAttempts(User user) {
  		String attemptsString = user.getUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, "0");
  		Integer attempts = 0;
  		try {
  			attempts = Integer.valueOf(attemptsString);
  		}
  