/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright 2007 - 2009 Pentaho Corporation. All rights reserved.
*
*/
package org.pentaho.platform.engine.security.userroledao.hibernate;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.pentaho.platform.engine.security.userroledao.AlreadyExistsException;
import org.pentaho.platform.engine.security.userroledao.IPentahoRole;
import org.pentaho.platform.engine.security.userroledao.IPentahoUser;
import org.pentaho.platform.engine.security.userroledao.IUserRoleDao;
import org.pentaho.platform.engine.security.userroledao.NotFoundException;
import org.pentaho.platform.engine.security.userroledao.PentahoRole;
import org.pentaho.platform.engine.security.userroledao.PentahoUser;
import org.pentaho.platform.engine.security.userroledao.UncategorizedUserRoleDaoException;
import org.pentaho.platform.engine.security.userroledao.messages.Messages;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
/**
* An {@link IUserRoleDao} that uses Hibernate. Furthermore, it uses Spring's <code>HibernateDaoSupport</code>. This
* allows instances to be dependency injected. At a minimum, instances require a Hibernate <code>SessionFactory</code>.
*
* <p>The <code>init</code> method must be called after all properties have been set and before calling any of the CRUD
* methods. Can be called automatically if using Spring via the <code>init-method</code> attribute. Otherwise, call it
* manually after setting all properties.</p>
*
* @author mlowery
*/
public class HibernateUserRoleDao extends HibernateDaoSupport implements IUserRoleDao {
// ~ Static fields/initializers ======================================================================================
public static final String DEFAULT_ALL_USERS_QUERY = "from PentahoUser order by username"; //$NON-NLS-1$
public static final String DEFAULT_ALL_ROLES_QUERY = "from PentahoRole order by name"; //$NON-NLS-1$
// ~ Instance fields =================================================================================================
private String allUsersQuery = DEFAULT_ALL_USERS_QUERY;
private String allRolesQuery = DEFAULT_ALL_ROLES_QUERY;
private InitHandler initHandler;
// ~ Constructors ====================================================================================================
public HibernateUserRoleDao() {
super();
}
// ~ Methods =========================================================================================================
/**
* A generic initialization method. Can be used to load initial data into user- and role-related tables.
*/
public void init() {
if (initHandler != null) {
initHandler.handleInit();
}
}
public void createUser(IPentahoUser userToCreate) throws AlreadyExistsException, UncategorizedUserRoleDaoException {
Assert.notNull(userToCreate, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0001_USER_CANNOT_BE_NULL")); //$NON-NLS-1$
Assert.hasLength(userToCreate.getUsername(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0002_USERNAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
Assert.notNull(userToCreate.getPassword(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0003_PASSWORD_CANNOT_BE_NULL")); //$NON-NLS-1$
if (getUser(userToCreate.getUsername()) == null) {
try {
getHibernateTemplate().save(userToCreate);
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
} else {
throw new AlreadyExistsException(userToCreate.getUsername());
}
}
public void deleteUser(IPentahoUser userToDelete) throws NotFoundException, UncategorizedUserRoleDaoException {
Assert.notNull(userToDelete, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0001_USER_CANNOT_BE_NULL")); //$NON-NLS-1$
Assert.hasLength(userToDelete.getUsername(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0002_USERNAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
IPentahoUser user = getUser(userToDelete.getUsername());
if (user != null) {
try {
getHibernateTemplate().delete(user);
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
} else {
throw new NotFoundException(userToDelete.getUsername());
}
}
public IPentahoUser getUser(String username) throws UncategorizedUserRoleDaoException {
Assert.hasLength(username, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0002_USERNAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
try {
return (PentahoUser) getHibernateTemplate().get(PentahoUser.class, username);
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
}
@SuppressWarnings("unchecked")
public List<IPentahoUser> getUsers() throws UncategorizedUserRoleDaoException {
try {
return getHibernateTemplate().find(getAllUsersQuery());
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
}
public void updateUser(IPentahoUser userToUpdate) throws NotFoundException, UncategorizedUserRoleDaoException {
Assert.notNull(userToUpdate, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0001_USER_CANNOT_BE_NULL")); //$NON-NLS-1$
Assert.hasLength(userToUpdate.getUsername(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0002_USERNAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
Assert.notNull(userToUpdate.getPassword(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0003_PASSWORD_CANNOT_BE_NULL")); //$NON-NLS-1$
if (getUser(userToUpdate.getUsername()) != null) {
try {
getHibernateTemplate().update(getHibernateTemplate().merge(userToUpdate));
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
} else {
throw new NotFoundException(userToUpdate.getUsername());
}
}
/**
* This method is more complex because this is the inverse end of a bidirectional many-to-many relationship. See
* Hibernate documentation section 6.3.2. Bidirectional associations. Basically, this means that the users set of this
* role must be managed manually.
*/
public void createRole(IPentahoRole roleToCreate) throws AlreadyExistsException, UncategorizedUserRoleDaoException {
Assert.notNull(roleToCreate, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0005_ROLE_CANNOT_BE_NULL")); //$NON-NLS-1$
Assert.hasLength(roleToCreate.getName(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0006_ROLE_NAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
if (getRole(roleToCreate.getName()) == null) {
try {
getHibernateTemplate().save(roleToCreate);
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
} else {
throw new AlreadyExistsException(roleToCreate.getName());
}
// manually manage users set
for (IPentahoUser user : roleToCreate.getUsers()) {
addUser(roleToCreate, user.getUsername());
}
}
/**
* This method is more complex because this is the inverse end of a bidirectional many-to-many relationship. See
* Hibernate documentation section 6.3.2. Bidirectional associations. Basically, this means that the users set of this
* role must be managed manually.
*/
public void deleteRole(IPentahoRole roleToDelete) throws NotFoundException, UncategorizedUserRoleDaoException {
Assert.notNull(roleToDelete, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0005_ROLE_CANNOT_BE_NULL")); //$NON-NLS-1$
Assert.hasLength(roleToDelete.getName(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0006_ROLE_NAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
IPentahoRole role = getRole(roleToDelete.getName());
if (role != null) {
try {
// for each user that is a member of this role, manually remove the role assignment from the user
for (IPentahoUser user : role.getUsers()) {
user.removeRole(role);
updateUser(user);
}
// delete the role itself now that it is no longer referenced anywhere
getHibernateTemplate().delete(role);
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
} else {
throw new NotFoundException(roleToDelete.getName());
}
}
public IPentahoRole getRole(String name) throws UncategorizedUserRoleDaoException {
Assert.hasLength(name, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0006_ROLE_NAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
try {
return (PentahoRole) getHibernateTemplate().get(PentahoRole.class, name);
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
}
@SuppressWarnings("unchecked")
public List<IPentahoRole> getRoles() throws UncategorizedUserRoleDaoException {
try {
return getHibernateTemplate().find(getAllRolesQuery());
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
}
/**
* This method is more complex because this is the inverse end of a bidirectional many-to-many relationship. See
* Hibernate documentation section 6.3.2. Bidirectional associations. Basically, this means that the users set of this
* role must be managed manually.
*/
@SuppressWarnings("unchecked")
public void updateRole(IPentahoRole roleToUpdate) throws NotFoundException, UncategorizedUserRoleDaoException {
Assert.notNull(roleToUpdate, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0005_ROLE_CANNOT_BE_NULL")); //$NON-NLS-1$
Assert.hasLength(roleToUpdate.getName(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0006_ROLE_NAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
IPentahoRole originalRole = getRole(roleToUpdate.getName());
// make a copy of originalRole's users since the merge call below will change the users
Set<IPentahoUser> originalRoleUsers = new HashSet<IPentahoUser>(originalRole.getUsers());
if (originalRole != null) {
try {
getHibernateTemplate().update(getHibernateTemplate().merge(roleToUpdate));
} catch (DataAccessException e) {
throw new UncategorizedUserRoleDaoException(Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0004_DATA_ACCESS_EXCEPTION"), e); //$NON-NLS-1$
}
} else {
throw new NotFoundException(roleToUpdate.getName());
}
// manually manage users set
// use relative complement (aka set-theoretic difference, aka subtraction) to get the users to add and users to
// remove
Set<IPentahoUser> usersToAdd = new HashSet<IPentahoUser>(CollectionUtils.subtract(roleToUpdate.getUsers(),
originalRoleUsers));
Set<IPentahoUser> usersToRemove = new HashSet<IPentahoUser>(CollectionUtils.subtract(originalRoleUsers,
roleToUpdate.getUsers()));
for (IPentahoUser user : usersToAdd) {
addUser(roleToUpdate, user.getUsername());
}
for (IPentahoUser user : usersToRemove) {
removeUser(roleToUpdate, user.getUsername());
}
}
/**
* This method is necessary because this is the inverse end of a bidirectional many-to-many relationship. See
* Hibernate documentation section 6.3.2. Bidirectional associations.
*/
protected void addUser(IPentahoRole roleToUpdate, String username) throws NotFoundException,
UncategorizedUserRoleDaoException {
Assert.notNull(roleToUpdate, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0005_ROLE_CANNOT_BE_NULL")); //$NON-NLS-1$
Assert.hasLength(roleToUpdate.getName(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0006_ROLE_NAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
Assert.hasLength(username, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0002_USERNAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
IPentahoUser user = getUser(username);
if (user != null) {
user.addRole(roleToUpdate);
updateUser(user);
} else {
throw new NotFoundException(username);
}
}
/**
* This method is necessary because this is the inverse end of a bidirectional many-to-many relationship. See
* Hibernate documentation section 6.3.2. Bidirectional associations.
*/
protected void removeUser(IPentahoRole roleToUpdate, String username) throws NotFoundException,
UncategorizedUserRoleDaoException {
Assert.notNull(roleToUpdate, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0005_ROLE_CANNOT_BE_NULL")); //$NON-NLS-1$
Assert.hasLength(roleToUpdate.getName(), Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0006_ROLE_NAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
Assert.hasLength(username, Messages.getInstance().getString("HibernateUserRoleDao.ERROR_0002_USERNAME_CANNOT_BE_BLANK")); //$NON-NLS-1$
IPentahoUser user = getUser(username);
if (user != null) {
user.removeRole(roleToUpdate);
updateUser(user);
} else {
throw new NotFoundException(username);
}
}
public void setAllUsersQuery(String allUsersQuery) {
Assert.hasLength(allUsersQuery, Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0007_ALL_USERS_QUERY_CANNOT_BE_BLANK")); //$NON-NLS-1$
this.allUsersQuery = allUsersQuery;
}
public String getAllUsersQuery() {
return allUsersQuery;
}
public void setAllRolesQuery(String allRolesQuery) {
Assert.hasLength(allUsersQuery, Messages.getInstance()
.getString("HibernateUserRoleDao.ERROR_0008_ALL_ROLES_QUERY_CANNOT_BE_BLANK")); //$NON-NLS-1$
this.allRolesQuery = allRolesQuery;
}
public String getAllRolesQuery() {
return allRolesQuery;
}
public void setInitHandler(InitHandler initHandler) {
this.initHandler = initHandler;
}
/**
* Generic interface to allow extensibility without tight coupling. Example use: insert sample users and roles into
* empty tables.
*
* @author mlowery
*/
public static interface InitHandler {
void handleInit();
}
}