systemic.sif.sbpframework.persist.dao.DOCacheDAO.java Source code

Java tutorial

Introduction

Here is the source code for systemic.sif.sbpframework.persist.dao.DOCacheDAO.java

Source

/*
 * DOCacheDAO.java
 * Created: 14/10/2011
 *
 * Copyright 2011 Systemic Pty Ltd
 * 
 * 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.
 */

package systemic.sif.sbpframework.persist.dao;

import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.persistence.PersistenceException;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.Restrictions;

import systemic.sif.sbpframework.persist.common.BasicTransaction;
import systemic.sif.sbpframework.persist.common.HibernateUtil;
import systemic.sif.sbpframework.persist.model.DOCObject;
import systemic.sif.sbpframework.persist.model.DOCache;
import au.com.systemic.framework.utils.StringUtils;

/**
 * @author Joerg Huber
 *
 */
public class DOCacheDAO extends BaseDAO {

    protected final Logger logger = Logger.getLogger(getClass());

    /**
    * This method attempts to retrieve a cached SIF Object based on the object name, object key (flatten key) and the
    * agentId. Since each object can only be provided from one zone the zoneID is irrelevant and is not required to
    * determine if an object is already cached. If the object isn't cached then null is returned.
    *
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
    * @param sifObjectName the name of the SIF Object for which the cached object info shall be returned.
    * @param flatKey A flattened key of the cached object that makes this object unique.
    * @param applicationId The application ID for which the cached object shall be returned. 
    * @param zoneId The zone ID for which the cached object shall be returned. 
    * @param loadAll TRUE=>Load all children elements (lazy loading forced). FALSE no children objects are loaded. 
    * 
     * @return The DOCache object if it exists in the cache already, null if it does not exist.
     * 
    * @throws IllegalArgumentException: Any of the arguments is null.
    * @throws PersistenceException: There is an issue with the underlying database. An error is logged.
    */
    @SuppressWarnings("unchecked")
    public DOCache retrieveCachedObject(BasicTransaction tx, String sifObjectName, String flatKey,
            String applicationId, String zoneId, boolean loadAll)
            throws IllegalArgumentException, PersistenceException {
        if (StringUtils.isEmpty(sifObjectName) || StringUtils.isEmpty(flatKey) || StringUtils.isEmpty(applicationId)
                || StringUtils.isEmpty(zoneId)) {
            throw new IllegalArgumentException(
                    "sifObjecttName, flatKey, applicationId or zoneId is empty or null.");
        }

        try {
            Criteria criteria = tx.getSession().createCriteria(DOCache.class)
                    .add(Restrictions.eq("sifObjectName", sifObjectName))
                    .add(Restrictions.eq("objectKeyValue", flatKey))
                    .add(Restrictions.eq("applicationId", applicationId)).add(Restrictions.eq("zoneId", zoneId));

            List<DOCache> cachedObjectList = criteria.list();

            // There can only be a maximum of one
            if (cachedObjectList.isEmpty()) // not in cache, yet
            {
                logger.debug("No entry in DOC for application = '" + applicationId + "', Sif Object = '"
                        + sifObjectName + "', zone = '" + zoneId + "' and flattened Key = '" + flatKey + "'.");
                return null;
            } else // already cached
            {
                DOCache cachedObject = cachedObjectList.get(0);

                if (loadAll) {
                    HibernateUtil.loadSubObject(cachedObject.getDependentObjects());
                }
                return cachedObject;
            }
        } catch (HibernateException e) {
            throw new PersistenceException(
                    "Unable to retrieve DOCache for application = '" + applicationId + "', Sif Object = '"
                            + sifObjectName + "', zone = '" + zoneId + "' and flattened Key = '" + flatKey + "'.",
                    e);
        }
    }

    /**
     * This method saves the given object to the DB. all sub-elements are saved as well. After the save the cacheObject
     * will have a new ID if it is a new object.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param cacheObject The object to save to the cache.
     * 
     * @throws IllegalArgumentException  cacheObject parameter is null.
     * @throws PersistenceException      A database error occurred.
     */
    public void save(BasicTransaction tx, DOCache cacheObject)
            throws IllegalArgumentException, PersistenceException {
        if (cacheObject == null) {
            throw new IllegalArgumentException("cacheObject is null.");
        }

        try {
            tx.getSession().saveOrUpdate(cacheObject);
        } catch (HibernateException e) {
            throw new PersistenceException("Unable to save cacheObject " + cacheObject + ".", e);
        }
    }

    /**
     * This method saves the given object to the DB. all sub-elements are saved as well. After the save the cacheObject
     * will have a new ID if it is a new object.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param docObject The object to save to the cache.
     * 
     * @throws IllegalArgumentException  cacheObject parameter is null.
     * @throws PersistenceException      A database error occurred.
     */
    public void save(BasicTransaction tx, DOCObject docObject)
            throws IllegalArgumentException, PersistenceException {
        if (docObject == null) {
            throw new IllegalArgumentException("docObject is null.");
        }

        try {
            tx.getSession().saveOrUpdate(docObject);
        } catch (HibernateException e) {
            throw new PersistenceException("Unable to save dependent object " + docObject + ".", e);
        }
    }

    /**
     * This method checks if the 'objectToTest' has already been cached for a requested. If so the object is returned
     * so that it can be re-used in another's cached object dependent list. If the object has not yet been cached
     * then null is returned.
     * The 'objectToTest' must hold the following properties for this method to succeed:
     *  - sifObjectName
     *  - applicationId
    *  - objectKeyValue
    *  - zoneId
     * If one of the above property is empty or null then a IllegalArgumentException is raised.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param objectToTest The object to find in the DOC.
     * 
     * @return See description.
     * 
     * @throws IllegalArgumentException  objectToTest is null or any of the properties listed above is empty or null.
     * @throws PersistenceException      A database error occurred.
     */
    public DOCObject getCachedDependentObject(BasicTransaction tx, DOCObject objectToTest)
            throws IllegalArgumentException, PersistenceException {
        return retrieve(tx, objectToTest);
    }

    /**
     * This method returns a list of dependent objects that have already been cached for a given SIF Object. If there are
     * no dependent objects that already been cached then an empty list is returned. If the object defined by its parameters
     * is not cached at all then null is returned and an info message is logged.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param sifObjectName The object name for which the dependent object list with already cached objects shall be returned.
     * @param flatKey The key of the above object.
     * @param applicationId The application Id for which the object is registered in the cache.
     * @param zoneId The zone Id for which the object is registered in the cache.
     * 
     * @return See description
     * 
    * @throws IllegalArgumentException: Any of the arguments is null.
    * @throws PersistenceException: There is an issue with the underlying database. An error is logged.
     */
    public Set<DOCObject> getAlreadyCachedDependentObjects(BasicTransaction tx, String sifObjectName,
            String flatKey, String applicationId, String zoneId)
            throws IllegalArgumentException, PersistenceException {
        // Get the object from the cache.
        DOCache cachedObject = retrieveCachedObject(tx, sifObjectName, flatKey, applicationId, zoneId, true);
        if (cachedObject == null) {
            return null;
        }

        return cachedObject.getDependentObjects();
    }

    /**
     * This method removes the docObject from the DO cache. Since there can be many objects in the DO Cache that 
     * depend on the given object all these need to be updated accordingly.
     * For this method to work, the following properties in the docObject must be set:
     *  - sifObjectName
     *  - applicationId
     *  - objectKeyValue
     *  - zoneId
     *  If the object is not cached as a dependent object then no action is taken.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param docObject The object for which the dependencies shall be returned.
     * 
     * @throws IllegalArgumentException  docObject is null or any of the properties listed above is empty or null.
     * @throws PersistenceException      A database error occurred.
     */
    @SuppressWarnings("unchecked")
    public void removeDependency(BasicTransaction tx, DOCObject docObject)
            throws IllegalArgumentException, PersistenceException {
        DOCObject depObj = retrieve(tx, docObject);
        if (depObj != null) // there are waiting objects for this one.
        {
            Criteria criteria = tx.getSession().createCriteria(DOCache.class)
                    .createAlias("dependentObjects", "depObj")
                    .add(Restrictions.eq("depObj.sifObjectName", docObject.getSifObjectName()))
                    .add(Restrictions.eq("depObj.objectKeyValue", docObject.getObjectKeyValue()))
                    .add(Restrictions.eq("depObj.applicationId", docObject.getApplicationId()))
                    .add(Restrictions.eq("depObj.zoneId", docObject.getZoneId()));

            List<DOCache> cachedObjectList = criteria.list();
            if (!cachedObjectList.isEmpty()) {
                for (DOCache parentObj : cachedObjectList) {
                    // remove depObj from parents dependent object list
                    parentObj.getDependentObjects().remove(depObj);
                    parentObj.setRemainingDependencies(parentObj.getDependentObjects().size());
                    tx.getSession().saveOrUpdate(parentObj);
                }
            }

            // This is possibly not required because of the 'cascade=all,delete-orphan' in the hibernate mapping
            tx.getSession().delete(depObj);
        }
    }

    /**
     * This method returns a list of DOC Objects. The objects are those that have not been requested, yet (requested=false).
     * 
     * Note: 
     * If an agent subscribes to more than one zone for the same sifObject then this method must be called
     * separately for each zone by the agent. If there are no cached and not yet requested objects for the
     * given parameters then an empty list is returned.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param sifObjectName The SIF Object names to search for (i.e StudentPersonal)
     * @param applicationId Only return objects marked for this application.
     * @param zoneId Only return objects marked for this zone.
     * 
     * @return See description
     * 
     * @throws IllegalArgumentException  Any of the parameters is null or empty.
     * @throws PersistenceException      A database error occurred.
     */
    @SuppressWarnings("unchecked")
    public List<DOCObject> getNotYetRequestedObjects(BasicTransaction tx, String sifObjectName,
            String applicationId, String zoneId) throws IllegalArgumentException, PersistenceException {
        if (StringUtils.isEmpty(sifObjectName) || StringUtils.isEmpty(applicationId)
                || StringUtils.isEmpty(zoneId)) {
            throw new IllegalArgumentException(
                    "Some of the following parameters are either null or empty: sifObjectName, applicationId, zoneId");
        }
        try {
            Criteria criteria = tx.getSession().createCriteria(DOCObject.class)
                    .add(Restrictions.eq("sifObjectName", sifObjectName))
                    .add(Restrictions.eq("applicationId", applicationId)).add(Restrictions.eq("zoneId", zoneId))
                    .add(Restrictions.eq("requested", Boolean.FALSE));

            return criteria.list();
        } catch (Exception ex) {
            throw new PersistenceException("Unable to retrieve list of DOCObjects for application = '"
                    + applicationId + "', Sif Object = '" + sifObjectName + "', zone = '" + zoneId + "'.", ex);
        }
    }

    /**
     * This method gets all cached objects of a given type for a particular application and agent that have no 
     * remaining dependencies. These are the candidates do be removed later and be processed by the appropriate 
     * subscriber. If there are no Cached Objects without dependencies then an empty list is returned.
     *  
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param sifObjectName The type of cached objects to be returned.
     * @param applicationId The application for which the objects shall be returned.
     * @param agentId The agent for which to get the object list. 
     * 
     * @return See description.
     * 
     * @throws IllegalArgumentException  Any of the parameters is null or empty.
     * @throws PersistenceException      A database error occurred. Error is logged.
     */
    @SuppressWarnings("unchecked")
    public List<DOCache> getObjectsWithoutDependencies(BasicTransaction tx, String sifObjectName,
            String applicationId, String agentId) throws IllegalArgumentException, PersistenceException {
        if (StringUtils.isEmpty(sifObjectName) || StringUtils.isEmpty(applicationId)
                || StringUtils.isEmpty(agentId)) {
            throw new IllegalArgumentException(
                    "Some of the following parameters are either null or empty: sifObjectName, applicationId, agentId");
        }
        try {
            Criteria criteria = tx.getSession().createCriteria(DOCache.class)
                    .add(Restrictions.eq("sifObjectName", sifObjectName))
                    .add(Restrictions.eq("applicationId", applicationId)).add(Restrictions.eq("agentId", agentId))
                    .add(Restrictions.eq("remainingDependencies", 0));

            return criteria.list();
        } catch (Exception ex) {
            throw new PersistenceException(
                    "Unable to retrieve list of Cached Objects without dependencies for application = '"
                            + applicationId + "', Sif Object = '" + sifObjectName + "'and agent = '" + agentId
                            + "'.",
                    ex);
        }
    }

    /**
     * This method returns all objects in the cache that have remaining dependencies but have an expiry date
     * older than the current date and time. These are the candidates that need to either be removed or 
     * re-requested by the agent. If there are no expired objects then an empty list is returned.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param applicationId Only return expired objects for this application.
     * @param agentId Only return expired object that were initially cached by this agent.
     * 
     * @return See description.
     * 
     * @throws IllegalArgumentException  Any of the parameters is null or empty.
     * @throws PersistenceException      A database error occurred. Error is logged.
     */
    @SuppressWarnings("unchecked")
    public List<DOCache> getExpiredObjects(BasicTransaction tx, String applicationId, String agentId)
            throws IllegalArgumentException, PersistenceException {
        if (StringUtils.isEmpty(applicationId) || StringUtils.isEmpty(agentId)) {
            throw new IllegalArgumentException(
                    "Some of the following parameters are either null or empty: applicationId, agentId");
        }
        try {
            Criteria criteria = tx.getSession().createCriteria(DOCache.class)
                    .add(Restrictions.eq("applicationId", applicationId)).add(Restrictions.eq("agentId", agentId))
                    .add(Restrictions.gt("remainingDependencies", 0))
                    .add(Restrictions.le("expiryDate", new Date()));

            return criteria.list();
        } catch (Exception ex) {
            throw new PersistenceException("Unable to retrieve list of expired Cached Objects for application = '"
                    + applicationId + "'and agent = '" + agentId + "'.", ex);
        }
    }

    /**
     * This method removes the given cached dependent object. If the object is null then no action is taken. The
     * object's id property must be set for this method to work.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param docObject The object to remove from the DOC_OBJECT table.
     * 
     * @throws PersistenceException      A database error occurred.
     */
    public void removeDependentObject(BasicTransaction tx, DOCObject docObject) throws PersistenceException {
        try {
            if ((docObject != null) && (docObject.getId() != null) && (docObject.getId().longValue() > 0)) {
                tx.getSession().delete(docObject);
            }
        } catch (HibernateException e) {
            throw new PersistenceException("Unable to remove dependent object " + docObject + ".", e);
        }
    }

    /**
     * This method removes the given object and all its dependencies from the cache. If the object is null then
     * no action is taken.
     * 
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     * @param cacheObject The object to remove from the DO Cache.
     * 
     * @throws PersistenceException      A database error occurred.
     */
    public void removeCachedObject(BasicTransaction tx, DOCache cacheObject) throws PersistenceException {
        try {
            if (cacheObject != null) {
                Set<DOCObject> depObjectList = cacheObject.getDependentObjects();
                tx.getSession().delete(cacheObject);

                if (depObjectList != null) {
                    for (DOCObject depObj : depObjectList) {
                        // If the number of parents is 1 then this is the last dependency and it can be removed.
                        if (depObj.getParents().size() <= 1) {
                            removeDependentObject(tx, depObj);
                        }
                    }
                }
            }
        } catch (HibernateException e) {
            throw new PersistenceException("Unable to remove cacheObject " + cacheObject + ".", e);
        }
    }

    /*---------------------*/
    /*-- Private methods --*/
    /*---------------------*/

    /*
     * For this method to work, the following properties in the docObject must be set:
     *  - sifObjectName
     *  - applicationId
     *  - objectKeyValue
     *  - zoneId
     *  If the object is not cached as a dependent object then no action is taken.
     *  
    * @param tx The Transaction within this method shall operate. MUST NOT BE NULL!
     *  
    * @throws IllegalArgumentException  docObject is null or any of the properties listed above is empty or null.
    * @throws PersistenceException      A database error occurred.
    */
    @SuppressWarnings("unchecked")
    public DOCObject retrieve(BasicTransaction tx, DOCObject docObject)
            throws IllegalArgumentException, PersistenceException {
        if (docObject == null) {
            throw new IllegalArgumentException("docObject == null! Not allowed.");
        }
        if (StringUtils.isEmpty(docObject.getSifObjectName()) || StringUtils.isEmpty(docObject.getApplicationId())
                || StringUtils.isEmpty(docObject.getZoneId())
                || StringUtils.isEmpty(docObject.getObjectKeyValue())) {
            throw new IllegalArgumentException(
                    "Some of the following properties in the 'docObject' are empty or null: sifObjectName, applicationId, zoneId, objectKeyValue");
        }

        Criteria criteria = tx.getSession().createCriteria(DOCObject.class)
                .add(Restrictions.eq("sifObjectName", docObject.getSifObjectName()))
                .add(Restrictions.eq("objectKeyValue", docObject.getObjectKeyValue()))
                .add(Restrictions.eq("applicationId", docObject.getApplicationId()))
                .add(Restrictions.eq("zoneId", docObject.getZoneId()));

        List<DOCObject> cachedObjectList = criteria.list();

        // There can only be a maximum of one
        if (cachedObjectList.isEmpty()) {
            return null; // No dependency on this object => return null.
        } else {
            return cachedObjectList.get(0);
        }
    }

}