Java tutorial
/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2013,2014,2015 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.database.dao; import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.support.DataAccessUtils; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.stereotype.Repository; import fr.gael.dhus.database.dao.interfaces.DaoEvent; import fr.gael.dhus.database.dao.interfaces.DaoListener; import fr.gael.dhus.database.dao.interfaces.DaoUtils; import fr.gael.dhus.database.dao.interfaces.HibernateDao; import fr.gael.dhus.database.dao.interfaces.UserListener; import fr.gael.dhus.database.object.Collection; import fr.gael.dhus.database.object.FileScanner; import fr.gael.dhus.database.object.Preference; import fr.gael.dhus.database.object.Role; import fr.gael.dhus.database.object.Search; import fr.gael.dhus.database.object.User; import fr.gael.dhus.database.object.restriction.AccessRestriction; import fr.gael.dhus.database.object.restriction.LockedAccessRestriction; import fr.gael.dhus.database.object.restriction.TmpUserLockedAccessRestriction; import fr.gael.dhus.server.ScalabilityManager; import fr.gael.dhus.service.exception.UserAlreadyExistingException; import fr.gael.dhus.spring.context.ApplicationContextProvider; import fr.gael.dhus.system.config.ConfigurationManager; @Repository public class UserDao extends HibernateDao<User, String> { @Autowired private CollectionDao collectionDao; @Autowired private ConfigurationManager cfgManager; @Autowired private ProductCartDao productCartDao; @Autowired private SearchDao searchDao; @Autowired private AccessRestrictionDao accessRestrictionDao; @Autowired private FileScannerDao fileScannerDao; @Autowired private ScalabilityManager scalabilityManager; /** * Unique public data user. */ private User publicData; /** * Public data username. */ private final String publicDataName = "public data"; public User getByName(final String name) { User user = (User) DataAccessUtils .uniqueResult(getHibernateTemplate().find("From User u where u.username=?", name)); // Optimization user extraction: most of the users uses case-sensitive // match for the login. A Requirement of the project asked for non-case // sensitive match. The extraction of non-case sensitive login from // database requires conversions and forbid the usage of indexes, so it // is much more slow. // This Fix aims to first try the extraction of the user with exact match // equals operator, then if not match use the toLower conversion. if (user == null) user = (User) DataAccessUtils.uniqueResult( getHibernateTemplate().find("From User u where lower(u.username)=lower(?)", name)); return user; } @Override public void delete(final User user) { if (user == null) return; // remove user external references final String uid = user.getUUID(); productCartDao.deleteCartOfUser(user); getHibernateTemplate().execute(new HibernateCallback<Void>() { @Override public Void doInHibernate(Session session) throws HibernateException, SQLException { String sql = "DELETE FROM COLLECTION_USER_AUTH WHERE USERS_UUID = :uid"; SQLQuery query = session.createSQLQuery(sql); query.setString("uid", uid); query.executeUpdate(); return null; } }); getHibernateTemplate().execute(new HibernateCallback<Void>() { @Override public Void doInHibernate(Session session) throws HibernateException, SQLException { String sql = "DELETE FROM PRODUCT_USER_AUTH WHERE USERS_UUID = :uid"; SQLQuery query = session.createSQLQuery(sql); query.setString("uid", uid); query.executeUpdate(); return null; } }); getHibernateTemplate().execute(new HibernateCallback<Void>() { @Override public Void doInHibernate(Session session) throws HibernateException, SQLException { String sql = "UPDATE PRODUCTS SET OWNER_UUID = NULL " + "WHERE OWNER_UUID = :uid"; SQLQuery query = session.createSQLQuery(sql); query.setString("uid", uid); query.executeUpdate(); return null; } }); getHibernateTemplate().execute(new HibernateCallback<Void>() { @Override public Void doInHibernate(Session session) throws HibernateException, SQLException { String sql = "DELETE FROM NETWORK_USAGE WHERE USER_UUID = :uid"; SQLQuery query = session.createSQLQuery(sql); query.setString("uid", uid); query.executeUpdate(); return null; } }); fireDeletedEvent(new DaoEvent<User>(user)); super.delete(user); } public void removeUser(User user) { user.setDeleted(true); getHibernateTemplate().update(user); productCartDao.deleteCartOfUser(user); try { fireDeletedEvent(new DaoEvent<User>(user)); } catch (Exception ex) { logger.error("Exception occured in listener", ex); } } private void forceDelete(User user) { super.delete(read(user.getUUID())); } @SuppressWarnings("unchecked") public List<User> scrollNotDeleted(final int skip, final int top) { // FIXME never call return getHibernateTemplate().execute(new HibernateCallback<List<User>>() { @Override public List<User> doInHibernate(Session session) throws HibernateException, SQLException { String hql = "FROM User WHERE deleted = false AND not username = " + "'" + cfgManager.getAdministratorConfiguration().getName() + " AND not username = '" + getPublicData().getUsername() + "'" + " ORDER BY username"; Query query = session.createQuery(hql).setReadOnly(true); query.setFirstResult(skip); query.setMaxResults(top); return (List<User>) query.list(); } }); } public Iterator<User> scrollForDataRight() { String filter = "WHERE deleted is false AND (not username = '" + cfgManager.getAdministratorConfiguration().getName() + "' ORDER BY username"; String query = "FROM " + entityClass.getName() + " " + filter; return new PagedIterator<User>(this, query); } @SuppressWarnings("unchecked") public List<User> readNotDeleted() { return (List<User>) find("FROM " + entityClass.getName() + " u WHERE u.deleted is false and " + "not u.username='" + cfgManager.getAdministratorConfiguration().getName() + "' " + "and not u.username LIKE '" + getPublicData().getUsername() + "' " + "order by username"); } public Iterator<User> scrollNotDeleted(final String filter, int skip) { StringBuilder query = new StringBuilder(); query.append("FROM ").append(entityClass.getName()).append(" "); query.append("WHERE deleted is false AND ").append("username LIKE'%").append(filter).append("%' AND "); query.append("not username='").append(getRootUser().getUsername()).append("' AND not username='") .append(getPublicDataName()).append("' "); query.append("ORDER BY username"); return new PagedIterator<>(this, query.toString(), skip, 3); } public Iterator<User> scrollForDataRight(String filter, int skip) { StringBuilder query = new StringBuilder(); query.append("FROM ").append(entityClass.getName()).append(" "); query.append("WHERE deleted is false AND username LIKE '%").append(filter).append("%' AND not username='") .append(cfgManager.getAdministratorConfiguration().getName()).append("' "); query.append("ORDER BY CASE username WHEN '").append(getPublicDataName()) .append("' THEN 1 ELSE 2 END, username"); return new PagedIterator<>(this, query.toString(), skip); } public Iterator<User> scrollAll(String filter, int skip) { StringBuilder query = new StringBuilder(); query.append("FROM ").append(entityClass.getName()).append(" "); query.append("WHERE username LIKE '%").append(filter).append("%' "); query.append("AND not username='").append(getPublicData().getUsername()).append("' "); query.append("ORDER BY username"); return new PagedIterator<>(this, query.toString(), skip); } public int countNotDeleted(String filter) { return DataAccessUtils.intResult(find("select count(*) FROM " + entityClass.getName() + " u WHERE u.deleted is false AND u.username LIKE '%" + filter + "%' and " + "not u.username='" + cfgManager.getAdministratorConfiguration().getName() + "'" + " and not u.username LIKE '" + getPublicData().getUsername() + "' ")); } public int countForDataRight(String filter) { return DataAccessUtils.intResult(find("select count(*) FROM " + entityClass.getName() + " u WHERE u.deleted is false AND u.username LIKE '%" + filter + "%' and " + "not u.username='" + cfgManager.getAdministratorConfiguration().getName() + "' ")); } public int countAll(String filter) { return DataAccessUtils .intResult(find("select count(*) FROM " + entityClass.getName() + " u WHERE u.username LIKE '%" + filter + "%'" + " and not u.username LIKE '" + getPublicData().getUsername() + "' ")); } public void addAccessToCollection(User user, Collection collection) { List<User> users = collectionDao.getAuthorizedUsers(collection); // Check is already granted for (User u : users) { if (u.getUUID().equals(user.getUUID())) { return; } } users.add(user); collection.setAuthorizedUsers(new HashSet<User>(users)); collectionDao.update(collection); } public void removeAccessToCollection(String user_uuid, Collection collection) { // if data are public, not possible to remove user right. if (cfgManager.isDataPublic()) { return; } List<User> users = collectionDao.getAuthorizedUsers(collection); // Check is already granted User selection = null; for (User u : users) { if (u.getUUID().equals(user_uuid)) { selection = u; } } if (selection != null) { users.remove(selection); collection.setAuthorizedUsers(new HashSet<User>(users)); collectionDao.update(collection); } } public String computeUserCode(User user) { if (user == null) throw new NullPointerException("Null user."); if (user.getUUID() == null) throw new IllegalArgumentException( "User " + user.getUsername() + " must be created in the DB to compute its code."); String digest = user.hash(); String code = user.getUUID() + digest; return code; } public User getUserFromUserCode(String code) { if (code == null) throw new NullPointerException("Null code."); String id = code.substring(0, 36); // Retrieve the user User user = read(id); if (user == null) throw new NullPointerException("User cannot be retrieved for id " + id); // Check the Id String hash = user.hash(); String user_hash = code.substring(36); if (!hash.equals(user_hash)) throw new SecurityException("Wrong hash code \"" + user_hash + "\"."); return user; } public void lockUser(User user, String reason) { LockedAccessRestriction ar = new LockedAccessRestriction(); ar.setBlockingReason(reason); user.addRestriction(ar); update(user); } public void unlockUser(User user, Class<? extends AccessRestriction> car) { if (user.getRestrictions() == null) return; Iterator<AccessRestriction> iter = user.getRestrictions().iterator(); HashSet<AccessRestriction> toDelete = new HashSet<AccessRestriction>(); while (iter.hasNext()) { AccessRestriction lar = iter.next(); if (lar.getClass().equals(car)) { iter.remove(); toDelete.add(lar); } } update(user); for (AccessRestriction restriction : toDelete) { accessRestrictionDao.delete(restriction); } } /** * Create a temporary user. * * @param temporary user. * @return the updated user. */ public void createTmpUser(User user) { TmpUserLockedAccessRestriction tuar = new TmpUserLockedAccessRestriction(); user.addRestriction(tuar); create(user); } @Override public User create(User u) { User user = getByName(u.getUsername()); if (user != null) { throw new UserAlreadyExistingException( "An user is already registered with name '" + u.getUsername() + "'."); } // Default new user come with at least search access role. if (u.getRoles().isEmpty()) { u.addRole(Role.SEARCH); u.addRole(Role.DOWNLOAD); } return super.create(u); } public void registerTmpUser(User u) { unlockUser(u, TmpUserLockedAccessRestriction.class); fireUserRegister(new DaoEvent<User>(u)); } public boolean isTmpUser(User u) { if (u.getRestrictions() == null) { return false; } for (AccessRestriction ar : u.getRestrictions()) { if (ar instanceof TmpUserLockedAccessRestriction) { return true; } } return false; } public void cleanupTmpUser(int max_days) { int skip = 0; final int top = DaoUtils.DEFAULT_ELEMENTS_PER_PAGE; long MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24; long runtime = System.currentTimeMillis(); final String hql = "SELECT u, r FROM User u LEFT OUTER JOIN " + "u.restrictions r WHERE r.discriminator = 'temporary'"; List<Object[]> result; HibernateTemplate template = getHibernateTemplate(); do { final int start = skip; result = template.execute(new HibernateCallback<List<Object[]>>() { @Override @SuppressWarnings("unchecked") public List<Object[]> doInHibernate(Session session) throws HibernateException, SQLException { Query query = session.createQuery(hql).setReadOnly(true); query.setFirstResult(start); query.setMaxResults(top); return (List<Object[]>) query.list(); } }); for (Object[] objects : result) { if (objects.length != 2) continue; User user = User.class.cast(objects[0]); TmpUserLockedAccessRestriction restriction = TmpUserLockedAccessRestriction.class.cast(objects[1]); long date = runtime - restriction.getLockDate().getTime(); if ((date / MILLISECONDS_PER_DAY) >= max_days) { logger.info("Remove unregistered User " + user.getUsername()); forceDelete(user); } } skip = skip + top; } while (result.size() == top); } public User getRootUser() { return getByName(cfgManager.getAdministratorConfiguration().getName()); } public boolean isRootUser(User user) { if (user.getUsername().equals(cfgManager.getAdministratorConfiguration().getName())) return true; return false; } void fireUserRegister(DaoEvent<User> e) { for (DaoListener<?> listener : getListeners()) { if (listener instanceof UserListener) ((UserListener) listener).register(e); } } // Preference settings private void updateUserPreference(User user) { getHibernateTemplate().update(user); } public void storeUserSearch(User user, String request, String footprint, HashMap<String, String> advanced, String complete) { Preference pref = user.getPreferences(); Search search = new Search(); search.setValue(request); search.setFootprint(footprint); search.setAdvanced(advanced); search.setComplete(complete); search.setNotify(false); search = searchDao.create(search); pref.getSearches().add(search); updateUserPreference(user); } public void removeUserSearch(User user, String uuid) { Search search = searchDao.read(uuid); if (search != null) { Preference pref = user.getPreferences(); Set<Search> s = pref.getSearches(); Iterator<Search> iterator = s.iterator(); while (iterator.hasNext()) { if (iterator.next().equals(search)) { iterator.remove(); } } updateUserPreference(user); } searchDao.delete(search); } public void activateUserSearchNotification(String uuid, boolean notify) { Search search = searchDao.read(uuid); search.setNotify(notify); searchDao.update(search); } public void clearUserSearches(User user) { Preference pref = user.getPreferences(); pref.getSearches().clear(); updateUserPreference(user); } public List<Search> getUserSearches(User user) { Set<Search> searches = read(user.getUUID()).getPreferences().getSearches(); List<Search> list = new ArrayList<Search>(searches); Collections.sort(list, new Comparator<Search>() { @Override public int compare(Search arg0, Search arg1) { return arg0.getValue().compareTo(arg1.getValue()); } }); return list; } // File Scanner preferences /** * Add a file scanner in user preferences, if it already exists, it is * updated otherwise, it is created and added. */ public FileScanner addFileScanner(User user, String url, String username, String password, String pattern, String cron_schedule, Set<Collection> collections) { FileScanner fs = null; boolean create = false; // if ( (fs = findFileScanner (user, url, username)) == null) // { fs = new FileScanner(); create = true; // } fs.setUrl(url); fs.setUsername(username); fs.setPassword(password); fs.setPattern(pattern); fs.setStatus(FileScanner.STATUS_ADDED); SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd MMMM yyyy - HH:mm:ss", Locale.ENGLISH); fs.setStatusMessage("Added on " + sdf.format(new Date())); fs.setCollections(collections); fs.setCronSchedule(cron_schedule); // Create and retrieve the fs ibnstance in DB; if (create) { fileScannerDao.create(fs); UserDao userDao = ApplicationContextProvider.getBean(UserDao.class); user = userDao.read(user.getUUID()); user.getPreferences().getFileScanners().add(fs); updateUserPreference(user); } else { fileScannerDao.update(fs); } return fs; } public void updateFileScanner(Long scan_id, String url, String username, String password, String pattern, String cron_schedule, Set<Collection> collections) { FileScanner fs = fileScannerDao.read(scan_id); fs.setUrl(url); fs.setUsername(username); fs.setPassword(password); fs.setPattern(pattern); fs.setStatus(FileScanner.STATUS_ADDED); SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd MMMM yyyy - HH:mm:ss", Locale.ENGLISH); fs.setStatusMessage("Updated on " + sdf.format(new Date())); fs.setCollections(collections); fs.setCronSchedule(cron_schedule); fileScannerDao.update(fs); } public void removeFileScanner(User user, Long scanner_id) { FileScanner fs = fileScannerDao.read(scanner_id); if (fs != null) { fileScannerDao.delete(fs); getHibernateTemplate().refresh(user); user.getPreferences().getFileScanners().remove(fs); updateUserPreference(user); } } public void setFileScannerActive(Long id, boolean active) { FileScanner fs = fileScannerDao.read(id); fs.setActive(active); fileScannerDao.update(fs); } public FileScanner findFileScanner(User user, String url, String username) { Set<FileScanner> fss = getFileScanners(user); for (FileScanner fs : fss) { /** * URL in not case sensitive instead of username is for ftp */ if (url.equalsIgnoreCase(fs.getUrl()) && username.equals(fs.getUsername())) { return fs; } } return null; } public Set<FileScanner> getFileScanners(User user) { return read(user.getUUID()).getPreferences().getFileScanners(); } public User getPublicData() { if (publicData != null) { return publicData; } publicData = getByName(getPublicDataName()); if (publicData == null && (!scalabilityManager.isActive() || scalabilityManager.isMaster())) { createPublicData(); } return publicData; } private void createPublicData() { publicData = new User(); publicData.setUsername(getPublicDataName()); publicData.setPassword("#"); publicData.setCreated(new Date()); publicData = create(publicData); } public String getPublicDataName() { return "~" + publicDataName; } /** * Get not deleted users for the given filter, offset and limit * @param filter * @param offset * @param limit * @return */ public Iterator<User> scrollNotDeletedByFilter(String filter, int skip) { String s = filter.toLowerCase(); StringBuilder sb = new StringBuilder(); sb.append("FROM ").append(entityClass.getName()).append(" "); sb.append("WHERE deleted is false AND "); sb.append("(username LIKE '%").append(s).append("%' ").append("OR lower(firstname) LIKE '%").append(s) .append("%' ").append("OR lower(lastname) LIKE '%").append(s).append("%' ") .append("OR lower(email) LIKE '%").append(s).append("%') "); sb.append("AND not username='").append(cfgManager.getAdministratorConfiguration().getName()) .append("' AND not username='").append(getPublicDataName()).append("' "); sb.append("ORDER BY username"); return new PagedIterator<>(this, sb.toString(), skip); } public int countNotDeletedByFilter(String filter) { return DataAccessUtils.intResult(find("select count(*) FROM " + entityClass.getName() + " u WHERE u.deleted is false AND (u.username LIKE '%" + filter + "%' OR lower(u.firstname) LIKE '%" + filter.toLowerCase() + "%' OR lower(u.lastname) LIKE '%" + filter.toLowerCase() + "%' OR lower(u.email) LIKE '%" + filter.toLowerCase() + "%') and not u.username='" + cfgManager.getAdministratorConfiguration().getName() + "'" + " and not u.username LIKE '" + getPublicData().getUsername() + "' ")); } /** * Retrieve the users list by (top,skip) page according to the passed filter * with configurable ordering. * @param filter the filter to run, if null, all the users will be returned. * @param order_by the order sequence, if null, default database order will be returned. * @param skip elements number to skip in the list. * @param top element kept in the list. * @return the list of filtered users. */ public List<User> getUsers(String filter, String order_by, final int skip, final int top) { // TODO Security on filter & orderBy string StringBuilder qBuilder = new StringBuilder(); // Scroll already add FROM entity class // qBuilder.append ("FROM User u "); // Just add the entity referer qBuilder.append(" u "); if (filter != null && !filter.isEmpty()) { qBuilder.append("WHERE "); qBuilder.append(filter); } // Builds the ORDER BY clause. if (order_by != null && !order_by.isEmpty()) { qBuilder.append(" ORDER BY "); qBuilder.append(order_by); } String hql = qBuilder.toString(); return scroll(hql, skip, top); } /** * Count the user according to the passed filter. * @param filter filter over users. If null all the users will be returned. * @return number of users. */ public int countUsers(String filter) { // TODO Security on filter string StringBuilder qBuilder = new StringBuilder(); qBuilder.append("SELECT count (*) FROM User u "); if (filter != null && !filter.isEmpty()) { qBuilder.append("WHERE "); qBuilder.append(filter); } return ((Long) getHibernateTemplate().find(qBuilder.toString()).get(0)).intValue(); } public Iterator<User> getAllUsers() { return new PagedIterator<>(this, "FROM " + entityClass.getName()); } }