org.eurekastreams.server.persistence.TabMapper.java Source code

Java tutorial

Introduction

Here is the source code for org.eurekastreams.server.persistence.TabMapper.java

Source

/*
 * Copyright (c) 2009-2010 Lockheed Martin Corporation
 *
 * 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 org.eurekastreams.server.persistence;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eurekastreams.commons.hibernate.QueryOptimizer;
import org.eurekastreams.server.domain.Gadget;
import org.eurekastreams.server.domain.Tab;
import org.eurekastreams.server.domain.TabTemplate;
import org.eurekastreams.server.persistence.exceptions.GadgetDeletionException;
import org.eurekastreams.server.persistence.exceptions.GadgetUndeletionException;

/**
 * This class provides the mapper functionality for Tab entities.
 */
public class TabMapper {
    /**
     * Constructor.
     * 
     * @param inQueryOptimizer
     *            the QueryOptimizer to use for specialized functions.
     */
    public TabMapper(final QueryOptimizer inQueryOptimizer) {
        queryOptimizer = inQueryOptimizer;
    }

    /**
     * The QueryOptimizer to use for specialized functions.
     */
    private QueryOptimizer queryOptimizer;

    /**
     * The entity manager to use for all ORM operations.
     */
    private EntityManager entityManager;

    /**
     * Default value for undeleteGadgetWindowInMinutes.
     */
    private final int defaultUndeleteGadgetWindowInMinutes = 20;

    /**
     * The number of minutes to allow a gadget to be undeleted in, defaults to 20 minutes.
     */
    private int undeleteGadgetWindowInMinutes = defaultUndeleteGadgetWindowInMinutes;

    /**
     * Logger instance for this class.
     */
    private Log logger = LogFactory.getLog(TabMapper.class);

    /**
     * Temp Method to get templates based on type. Note: this method only returns a single result, but queries all tab
     * templates. There is no way for the query to distinguish which tab template comes back.
     * 
     * @param type
     *            The TabType.
     * @return the TabTemplate.
     */
    public TabTemplate getTabTemplate(final String type) {
        Query q = entityManager.createQuery("FROM TabTemplate t where t.type=:tabTemplateType")
                .setParameter("tabTemplateType", type);
        return (TabTemplate) q.getSingleResult();
    }

    /**
     * Set the number of minutes to allow a gadget to be undeleted in.
     * 
     * @param undeleteWindowInMinutes
     *            the number of minutes to allow a tab to be undeleted in.
     */
    public void setUndeleteGadgetWindowInMinutes(final int undeleteWindowInMinutes) {
        this.undeleteGadgetWindowInMinutes = undeleteWindowInMinutes;
    }

    /**
     * Set the entity manager to use for all ORM operations.
     * 
     * @param inEntityManager
     *            the entity manager to use for all ORM operations.
     */
    @PersistenceContext
    public void setEntityManager(final EntityManager inEntityManager) {
        this.entityManager = inEntityManager;
    }

    /**
     * Find the Tab by id and eagerly load the gadget collection.
     * 
     * @param tabId
     *            ID of the Tab to look up.
     * 
     * @return the entity with the input
     */
    public Tab findById(final Long tabId) {
        Query q = entityManager
                .createQuery("from Tab t left join fetch t.template where t.id = :tabId and t.deleted = 'false'")
                .setParameter("tabId", tabId);
        Tab tab = (Tab) q.getSingleResult();

        // Touch the gadgets so that they will be eagerly loaded.
        tab.getGadgets().size();

        return tab;
    }

    /**
     * Find the parent Tab of the input Gadget id.
     * 
     * @param gadgetId
     *            the Gadget id to find the parent Tab for.
     * @return the parent Tab
     */
    public TabTemplate findByGadgetId(final Long gadgetId) {
        return getTabTemplateByGadgetId(gadgetId, false);
    }

    /**
     * This method retrieves a tab with only a gadget id to start with.
     * 
     * @param gadgetId
     *            - id of the gadget used to retrieve the container tab.
     * @return - container tab of the gadget id passed in.
     */
    public Tab findTabByGadgetId(final Long gadgetId) {
        Query q = entityManager.createQuery(
                "SELECT t FROM Tab t, Gadget g " + "WHERE g.id = :gadgetId " + "AND g.template.id = t.template.id")
                .setParameter("gadgetId", gadgetId);

        Tab tab = (Tab) q.getSingleResult();

        // Touch the gadgets so that they will be eagerly loaded.
        tab.getGadgets().size();

        return tab;
    }

    /**
     * Find the Tab by id.
     * 
     * @param tabId
     *            ID of the Tab to look up
     * @return the entity with the input
     */
    public Tab findById(final Integer tabId) {
        return findById(tabId.longValue());
    }

    /**
     * Update all entities that have changed since they were loaded within the same context.
     */
    public void flush() {
        entityManager.flush();
    }

    /**
     * Mark the input gadget as deleted so that it may be undeleted later on.
     * 
     * @param gadgetToDelete
     *            The gadget to delete.
     * @throws GadgetDeletionException
     *             thrown when the caller tries to delete a gadget from a tab that doesn't own the input gadget.
     */
    public void deleteGadget(final Gadget gadgetToDelete) throws GadgetDeletionException {

        // find the tab to which the input gadget belongs
        TabTemplate tab = null;
        try {
            tab = getTabTemplateByGadgetId(gadgetToDelete.getId(), false);
        } catch (Exception ex) {
            throw new GadgetDeletionException("Could not find either the specified Gadget or Tab",
                    gadgetToDelete.getId());
        }

        // remove the gadget from the collection
        if (tab.getGadgets().size() > 1) {
            tab.getGadgets().remove(gadgetToDelete);
            tab.getGadgets().add(gadgetToDelete);
        }

        // rearrange the gadgetIndexes of the other gadgets
        for (Gadget currentGadget : tab.getGadgets()) {
            if (currentGadget.getZoneNumber() == gadgetToDelete.getZoneNumber()
                    && currentGadget.getZoneIndex() > gadgetToDelete.getZoneIndex()) {
                currentGadget.setZoneIndex(currentGadget.getZoneIndex() - 1);
            }
        }

        // mark gadget as deleted, and re-attach it to the tab, because the
        // previous statement detatched it.
        markGadgetAsDeleted(tab, gadgetToDelete);

        // clean up all previously deleted gadgets that are no longer in the
        // undelete time frame.
        cleanUpDeletedGadgets();

    }

    /**
     * Implementation of the undelete method from the TabMapper interface.
     * 
     * @param gadgetId
     *            id of the gadget to be undeleted.
     * @return gadget object represented by the gadget id.
     * @throws GadgetUndeletionException
     *             thrown when error undeleting a gadget.
     */

    public Gadget undeleteGadget(final long gadgetId) throws GadgetUndeletionException {
        // make sure the gadget exists in the input tab
        TabTemplate template = null;
        try {
            template = getTabTemplateByGadgetId(gadgetId, true);
        } catch (Exception ex) {
            throw new GadgetUndeletionException(
                    "Could not find either the specified gadget or tab for gadgetId=" + gadgetId, gadgetId);
        }

        /* get the deleted gadget from the database */
        Gadget gadgetToUndelete = (Gadget) entityManager
                .createQuery("from Gadget where id = :gadgetId and deleted = true")
                .setParameter("gadgetId", gadgetId).getSingleResult();

        if (gadgetToUndelete == null) {
            throw new GadgetUndeletionException("Failure when trying to get gadget with id=" + gadgetId, gadgetId);
        }

        try {
            /*
             * bump up the zone index of each gadget in the same zone as the gadget to be undeleted
             */
            for (Gadget currentGadget : template.getGadgets()) {

                if (currentGadget.getZoneNumber() == gadgetToUndelete.getZoneNumber()
                        && currentGadget.getZoneIndex() >= gadgetToUndelete.getZoneIndex()) {
                    currentGadget.setZoneIndex(currentGadget.getZoneIndex() + 1);
                }
            }

            /* add the gadget back into the collection */
            template.getGadgets().add(gadgetToUndelete);

            /* update the status of the undeleted gadget in the database */
            entityManager
                    .createQuery("update versioned Gadget set deleted = false, "
                            + "dateDeleted = null, tabTemplateId = :tabId " + "where id = :gadgetId")
                    .setParameter("gadgetId", gadgetToUndelete.getId()).setParameter("tabId", template.getId())
                    .executeUpdate();

            return gadgetToUndelete;
        } catch (Exception ex) {
            throw new GadgetUndeletionException(
                    "An error occurred while trying to undelete the gadget with gadgetId=" + gadgetId, gadgetId);
        }
    }

    /**
     * Get the tab by gadget id.
     * 
     * @param gadgetId
     *            The id of the gadget to find the tab for.
     * 
     * @param isDeleted
     *            whether to look for deleted or undeleted gadget.
     * 
     * @return the TabGroup that owns the input tabId.
     */
    private TabTemplate getTabTemplateByGadgetId(final long gadgetId, final boolean isDeleted) {
        return (TabTemplate) entityManager
                .createQuery("select g.template from Gadget g where g.id = :gadgetId and g.deleted = :isDeleted")
                .setParameter("gadgetId", gadgetId).setParameter("isDeleted", isDeleted).getSingleResult();
    }

    /**
     * Mark the input gadget as deleted so that it's no longer returned in queries but can be undeleted later on. The
     * gadget would have just been removed from the gadget, so we need to set the tabId back to the gadget so that it's
     * ignored by the collection.
     * 
     * @param template
     *            The TabTemplate that contains the gadget.
     * @param gadget
     *            The gadget to mark as deleted.
     */
    private void markGadgetAsDeleted(final TabTemplate template, final Gadget gadget) {
        GregorianCalendar currentDateTime = new GregorianCalendar();

        entityManager
                .createQuery("update versioned Gadget set deleted = true, "
                        + "dateDeleted = :deletedTimestamp, tabTemplateId = :tabTemplateId "
                        + "where id = :gadgetId")
                .setParameter("deletedTimestamp", currentDateTime.getTime())
                .setParameter("gadgetId", gadget.getId()).setParameter("tabTemplateId", template.getId())
                .executeUpdate();
    }

    /**
     * Clean up deleted gadgets here using the expired date set earlier. Currently this is hard-coded to be at least 20
     * (configurable) minutes since the gadget was originally deleted, but could be much longer because it is dependent
     * on the next gadget that is deleted. If one gadget is deleted on Jan 1st and the next gadget is deleted on March
     * 1st, the 1st gadget will remain flagged as deleted in the database until March 1st so we definitely need a full
     * timestamp for this object.
     */
    private void cleanUpDeletedGadgets() {
        GregorianCalendar expiredDateTime = new GregorianCalendar();
        expiredDateTime.add(Calendar.MINUTE, -undeleteGadgetWindowInMinutes);

        entityManager
                .createQuery(
                        "delete from Gadget de where de.deleted = true " + "and de.dateDeleted < :expiredTimestamp")
                .setParameter("expiredTimestamp", expiredDateTime.getTime()).executeUpdate();
    }

    /**
     * This method is responsible for moving a gadget from one location to another on the any gadget page.
     * 
     * @param gadgetId
     *            - id of the gadget that is being moved.
     * @param sourceTabTemplateId
     *            - id of the tab template where the gadget is moving from.
     * @param sourceZoneIndex
     *            - index of the position in the zone that the gadget is moving from.
     * @param sourceZoneNumber
     *            - number of the zone that the gadget is moving from.
     * @param targetTabTemplateId
     *            - id of the tab template where the gadget is moving to.
     * @param targetZoneIndex
     *            - index of the position in the zone that the gadget is moving to.
     * @param targetZoneNumber
     *            - number of the zone that the gadget is moving to.
     */
    @SuppressWarnings("unchecked")
    public void moveGadget(final Long gadgetId, final Long sourceTabTemplateId, final Integer sourceZoneIndex,
            final Integer sourceZoneNumber, final Long targetTabTemplateId, final Integer targetZoneIndex,
            final Integer targetZoneNumber) {
        logger.debug("Moving gadget: " + gadgetId + " from tab templateid: " + sourceTabTemplateId + " zoneNumber: "
                + sourceZoneNumber + " zoneIndex: " + sourceZoneIndex + " To " + " tab templateid: "
                + targetTabTemplateId + " zoneNumber: " + targetZoneNumber + " zoneIndex: " + targetZoneIndex);

        // Source
        List<Gadget> sourceGadgets = (List<Gadget>) entityManager
                .createQuery("FROM Gadget g " + "WHERE g.template.id =:sourceTabTemplateId "
                        + "AND g.zoneNumber =:sourceZoneNumber AND g.deleted = false ORDER BY g.zoneIndex")
                .setParameter("sourceTabTemplateId", sourceTabTemplateId)
                .setParameter("sourceZoneNumber", sourceZoneNumber).getResultList();

        Gadget movingGadget = null;

        for (int i = 0; i < sourceGadgets.size(); i++) {
            if (sourceGadgets.get(i).getId() == gadgetId) {
                movingGadget = sourceGadgets.get(i);
                sourceGadgets.remove(i);
            }
        }

        // Target
        List<Gadget> targetGadgets = (List<Gadget>) entityManager
                .createQuery("FROM Gadget g " + "WHERE g.template.id =:targetTabTemplateId "
                        + "AND g.zoneNumber =:targetZoneNumber AND g.deleted = false ORDER BY g.zoneIndex")
                .setParameter("targetTabTemplateId", targetTabTemplateId)
                .setParameter("targetZoneNumber", targetZoneNumber).getResultList();

        // Remove from target as well
        for (int i = 0; i < targetGadgets.size(); i++) {
            if (targetGadgets.get(i).getId() == gadgetId) {
                targetGadgets.remove(i);
            }
        }

        if (targetZoneIndex < targetGadgets.size()) {
            targetGadgets.add(targetZoneIndex, movingGadget);
        } else {
            targetGadgets.add(movingGadget);
        }

        // Renumber Source gadgets
        for (int i = 0; i < sourceGadgets.size(); i++) {
            Gadget g = sourceGadgets.get(i);
            entityManager
                    .createQuery("UPDATE versioned Gadget g SET g.zoneIndex = :newZoneIndex WHERE g.id = :gadgetId")
                    .setParameter("newZoneIndex", i).setParameter("gadgetId", g.getId()).executeUpdate();
        }

        // Renumber target gadgets
        for (int i = 0; i < targetGadgets.size(); i++) {
            Gadget g = targetGadgets.get(i);
            entityManager
                    .createQuery("UPDATE versioned Gadget g SET g.zoneIndex = :newZoneIndex, "
                            + "g.template.id =:targetTabTemplateId, g.zoneNumber =:targetZoneNumber "
                            + "WHERE g.id = :gadgetId")
                    .setParameter("newZoneIndex", i).setParameter("gadgetId", g.getId())
                    .setParameter("targetTabTemplateId", targetTabTemplateId)
                    .setParameter("targetZoneNumber", targetZoneNumber).executeUpdate();
        }
    }
}