/* (c) 2015 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.geofence.core.dao.impl; import org.geoserver.geofence.core.dao.PrioritizableDAO; import com.googlecode.genericdao.search.Field; import com.googlecode.genericdao.search.ISearch; import java.util.List; import javax.persistence.Query; import com.googlecode.genericdao.search.Search; import org.geoserver.geofence.core.model.enums.InsertPosition; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.geoserver.geofence.core.model.Prioritizable; import org.geoserver.geofence.core.model.Rule; import org.springframework.transaction.annotation.Transactional; /** * Public implementation of the GSUserDAO interface * * @author Emanuele Tajariol (etj at geo-solutions.it) */ @Transactional(value = "geofenceTransactionManager") public abstract class PrioritizableDAOImpl<T extends Prioritizable> extends BaseDAO<T, Long> implements PrioritizableDAO<T> { private static final Logger LOGGER = LogManager.getLogger(PrioritizableDAOImpl.class); protected long persist(Class<T> clazz, T entity, InsertPosition position) { switch(position) { case FIXED: // priority is already set break; case FROM_START: Search search = new Search(clazz); search.setFirstResult((int)entity.getPriority()); search.setMaxResults(1); search.addSortAsc("priority"); List<Prioritizable> list = super._search(search); if(list.isEmpty()) { // no rule found at given position: let's find out why int count = count(new Search(clazz)); if(LOGGER.isDebugEnabled()) LOGGER.debug("No rule found at position " + entity.getPriority() + " -- rules count:"+count); if(count == 0) { // this is the first rule inserted if(LOGGER.isDebugEnabled()) LOGGER.debug("Inserting first rule"); entity.setPriority(1); // this is the only rule so far, let's put in an arbitrary value } else { // some rules in, the requested postion is at bottom Search s1 = new Search(clazz); s1.addField("priority", Field.OP_MAX); long maxPri = (Long)searchUnique(s1); entity.setPriority(maxPri+1); if(LOGGER.isDebugEnabled()) LOGGER.debug("Inserting rule in last position"); } } else { long basepri = list.get(0).getPriority(); if(LOGGER.isDebugEnabled()) { LOGGER.debug("shifting rules from priority " + basepri + " downward"); } int i = shift(clazz, basepri, 1); if(LOGGER.isDebugEnabled()) { LOGGER.debug("shifted "+i+" rules from priority " + basepri + " downward"); } entity.setPriority(basepri); } break; case FROM_END: // 0 based: if set to 0, this rule will go in last position long posFromEnd = entity.getPriority(); int count = count(new Search(clazz)); if(count == 0) { if(LOGGER.isDebugEnabled()) LOGGER.debug("Inserting first rule"); entity.setPriority(1); // this is the only rule so far, let's put in an arbitrary value } else { // needed for fetching the rule just before the new one // last rule is currently at position count - 1 (0-based) long posFromStart = count - posFromEnd -1; if(LOGGER.isDebugEnabled()) { LOGGER.debug("Inserting rule -- count:"+count+" pos:-"+posFromEnd+"+"+posFromStart); } if(posFromStart < 0 ) { throw new IllegalArgumentException("Bad position from end ("+posFromEnd+") with count="+count); } Search searchEnd = new Search(clazz); searchEnd.setFirstResult((int)posFromStart); searchEnd.setMaxResults(1); searchEnd.addSortAsc("priority"); List<Prioritizable> list1 = super._search(searchEnd); if(list1.isEmpty()) { // no rule found at given position: let's find out why throw new IllegalArgumentException("Bad position from end ("+posFromEnd+") with count="+count); } else { long basepri = list1.get(0).getPriority()+1; if(LOGGER.isDebugEnabled()) { LOGGER.debug("shifting rules from priority " + basepri + " downward"); } int i = shift(clazz, basepri, 1); if(LOGGER.isDebugEnabled()) { LOGGER.debug("shifted "+i+" rules from priority " + basepri + " downward"); } entity.setPriority(basepri); } } break; default: throw new IllegalArgumentException("Bad position type " + position); } persistInternal(entity); return entity.getPriority(); } /** * This is needed to call the proper persist() metod in the child class, * or automatic bean conversion in DAO will not be carried out. */ protected abstract void persistInternal(T rule); /** * Each subclass needs its own filtering. */ protected abstract Search getDupSearch(T rule); protected int shift(Class<T >clazz, long priorityStart, long offset) { if ( offset <= 0 ) { throw new IllegalArgumentException("Positive offset required"); } Search search = new Search(clazz); search.addFilterGreaterOrEqual("priority", priorityStart); search.addFilterLessThan("priority", priorityStart + offset); if ( super.count(search) == 0 ) { return -1; } String hql = "UPDATE "+clazz.getSimpleName()+" SET priority=priority+ :offset WHERE priority >= :priorityStart"; Query query = em().createQuery(hql); query.setParameter("offset", offset); query.setParameter("priorityStart", priorityStart); return query.executeUpdate(); } @Override public void swap(long id1, long id2) { T rule1 = super.find(id1); T rule2 = super.find(id2); if ( (rule1 == null) || (rule2 == null) ) { throw new IllegalArgumentException("Rule not found"); } Long tmp = rule1.getPriority(); rule1.setPriority(rule2.getPriority()); rule2.setPriority(tmp); super.merge(rule1, rule2); } @Override public List<T> search(ISearch search) { return super.search(search); } }