/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.server.dao; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import com.thoughtworks.go.domain.NullUser; import com.thoughtworks.go.domain.User; import com.thoughtworks.go.domain.Users; import com.thoughtworks.go.server.cache.GoCache; import com.thoughtworks.go.server.exceptions.UserEnabledException; import com.thoughtworks.go.server.exceptions.UserNotFoundException; import com.thoughtworks.go.server.transaction.TransactionSynchronizationManager; import com.thoughtworks.go.server.transaction.TransactionTemplate; import com.thoughtworks.go.util.StringUtil; import org.hibernate.Criteria; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionSynchronizationAdapter; @Component public class UserSqlMapDao extends HibernateDaoSupport implements UserDao { private SessionFactory sessionFactory; private TransactionTemplate transactionTemplate; private GoCache goCache; private final TransactionSynchronizationManager transactionSynchronizationManager; protected static final String ENABLED_USER_COUNT_CACHE_KEY = "ENABLED_USER_COUNT_CACHE_KEY".intern(); @Autowired public UserSqlMapDao(SessionFactory sessionFactory, TransactionTemplate transactionTemplate, GoCache goCache, TransactionSynchronizationManager transactionSynchronizationManager) { this.sessionFactory = sessionFactory; this.transactionTemplate = transactionTemplate; this.goCache = goCache; this.transactionSynchronizationManager = transactionSynchronizationManager; setSessionFactory(sessionFactory); } public void saveOrUpdate(final User user) { assertUserNotAnonymous(user); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { transactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { clearEnabledUserCountFromCache(); } }); sessionFactory.getCurrentSession().saveOrUpdate(copyLoginToDisplayNameIfNotPresent(user)); } }); } public User findUser(final String userName) { return (User) transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus transactionStatus) { User user = (User) sessionFactory.getCurrentSession() .createCriteria(User.class) .add(Restrictions.eq("name", userName)) .setCacheable(true).uniqueResult(); return user == null ? new NullUser() : user; } }); } public Users findNotificationSubscribingUsers() { return (Users) transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus transactionStatus) { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(User.class); criteria.setCacheable(true); criteria.add(Restrictions.isNotEmpty("notificationFilters")); return new Users(criteria.list()); } }); } public Users allUsers() { return new Users((List<User>) transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus transactionStatus) { Query query = sessionFactory.getCurrentSession().createQuery("FROM User"); query.setCacheable(true); return query.list(); } })); } public Integer enabledUserCount() { Integer value = (Integer) goCache.get(ENABLED_USER_COUNT_CACHE_KEY); if (value != null) { return value; } synchronized (ENABLED_USER_COUNT_CACHE_KEY) { value = (Integer) goCache.get(ENABLED_USER_COUNT_CACHE_KEY); if (value == null) { value = (Integer) hibernateTemplate().execute(new HibernateCallback<Object>() { @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { return session.createCriteria(User.class).add(Restrictions.eq("enabled", true)).setProjection(Projections.rowCount()).setCacheable(true).uniqueResult(); } }); goCache.put(ENABLED_USER_COUNT_CACHE_KEY, value); } return value; } } // Used only by Twist tests public void deleteAll() { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { transactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { clearEnabledUserCountFromCache(); } }); sessionFactory.getCurrentSession().createQuery("DELETE FROM User").executeUpdate(); } }); } public void disableUsers(List<String> usernames) { changeEnabledStatus(usernames, false); } public void enableUsers(List<String> usernames) { changeEnabledStatus(usernames, true); } public List<User> enabledUsers() { List<User> enabledUsers = new ArrayList<>(); for (User user : allUsers()) { if (user.isEnabled()) { enabledUsers.add(user); } } return enabledUsers; } public Set<String> findUsernamesForIds(final Set<Long> userIds) { List<User> users = allUsers(); Set<String> userNames = new HashSet<>(); for (User user : users) { if (userIds.contains(user.getId())) { userNames.add(user.getName()); } } return userNames; } public User load(final long id) { return (User) transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus transactionStatus) { return sessionFactory.getCurrentSession().load(User.class, id); } }); } @Override public boolean deleteUser(final String username) { return (Boolean) transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus status) { User user = findUser(username); if (user instanceof NullUser) { throw new UserNotFoundException(); } if (user.isEnabled()) { throw new UserEnabledException(); } sessionFactory.getCurrentSession().delete(user); return Boolean.TRUE; } }); } private void clearEnabledUserCountFromCache() { synchronized (ENABLED_USER_COUNT_CACHE_KEY) { goCache.remove(ENABLED_USER_COUNT_CACHE_KEY); } } protected HibernateTemplate hibernateTemplate() { return getHibernateTemplate(); } private void assertUserNotAnonymous(User user) { if (user.isAnonymous()) { throw new IllegalArgumentException(String.format("User name '%s' is not permitted.", user.getName())); } } private User copyLoginToDisplayNameIfNotPresent(User user) { if (StringUtil.isBlank(user.getDisplayName())) { user.setDisplayName(user.getName()); } return user; } private void changeEnabledStatus(final List<String> usernames, final boolean enabled) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { transactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { clearEnabledUserCountFromCache(); } }); String queryString = String.format("update %s set enabled = :enabled where name in (:userNames)", User.class.getName()); Query query = sessionFactory.getCurrentSession().createQuery(queryString); query.setParameter("enabled", enabled); query.setParameterList("userNames", usernames); query.executeUpdate(); } }); } }