Package eu.scape_project.planning.manager

Source Code of eu.scape_project.planning.manager.PlanManager$PlanQuery

/*******************************************************************************
* Copyright 2006 - 2014 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* 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 eu.scape_project.planning.manager;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import javax.ejb.Stateful;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.hibernate.Hibernate;
import org.slf4j.Logger;

import eu.scape_project.planning.exception.PlanningException;
import eu.scape_project.planning.model.AlternativesDefinition;
import eu.scape_project.planning.model.DigitalObject;
import eu.scape_project.planning.model.Plan;
import eu.scape_project.planning.model.PlanProperties;
import eu.scape_project.planning.model.PlanState;
import eu.scape_project.planning.model.User;
import eu.scape_project.planning.model.Values;
import eu.scape_project.planning.model.measurement.Measure;
import eu.scape_project.planning.model.transform.OrdinalTransformer;
import eu.scape_project.planning.model.transform.Transformer;
import eu.scape_project.planning.model.tree.Leaf;
import eu.scape_project.planning.model.tree.Node;
import eu.scape_project.planning.model.tree.ObjectiveTree;
import eu.scape_project.planning.model.tree.TreeNode;
import eu.scape_project.planning.utils.FacesMessages;

/**
* Stateful session bean for managing plans.
*/
@Stateful
@SessionScoped
@Named("planManager")
public class PlanManager implements Serializable {
    private static final long serialVersionUID = -1L;

    /**
     * Selection of projects to query.
     */
    public enum WhichProjects {
        ALLPROJECTS,
        PUBLICPROJECTS,
        MYPROJECTS;
    }

    @Inject
    private Logger log;

    @Inject
    private EntityManager em;

    @Inject
    private ByteStreamManager bytestreamManager;

    private WhichProjects lastLoadMode = WhichProjects.MYPROJECTS;

    @Inject
    private User user;

    @Inject
    private FacesMessages facesMessages;

    /**
     * Plan properties of all loaded plans in this session.
     */
    private HashSet<Integer> sessionPlans;

    /**
     * Query to get PlanProperties.
     */
    public class PlanQuery {

        private CriteriaBuilder builder;

        private CriteriaQuery<PlanProperties> cq;

        private Root<Plan> fromPlan;

        private Path<PlanProperties> fromPP;

        private List<Predicate> visibilityPredicates;
        private List<Predicate> stateFilterPredicates;
        private List<Predicate> nameFilterPredicates;
        private Predicate mappedFilterPredicate;

        private Predicate playgroundFilterPredicate;

        /**
         * Initializes the query.
         */
        private void init() {
            builder = em.getCriteriaBuilder();

            cq = builder.createQuery(PlanProperties.class);

            // From
            fromPlan = cq.from(Plan.class);
            fromPP = fromPlan.<PlanProperties> get("planProperties");

            // Select
            cq.select(fromPlan.<PlanProperties> get("planProperties"));

            visibilityPredicates = new ArrayList<Predicate>(2);
            stateFilterPredicates = new ArrayList<Predicate>();
            nameFilterPredicates = new ArrayList<Predicate>();
        }

        /**
         * Adds a visibility criteria to the query.
         *
         * @param whichProjects
         *            criteria to add
         * @return this plan query
         */
        public PlanQuery addVisibility(WhichProjects whichProjects) {
            // Select usernames of the users group
            if (whichProjects == WhichProjects.MYPROJECTS) {
                Subquery<User> subquery = cq.subquery(User.class);
                Root<User> fromUser = subquery.from(User.class);
                subquery.select(fromUser.<User> get("username"));
                subquery.where(builder.equal(fromUser.get("userGroup"), user.getUserGroup()));

                visibilityPredicates.add(fromPP.get("owner").in(subquery));
            } else if (whichProjects == WhichProjects.PUBLICPROJECTS) {
                visibilityPredicates.add(builder.or(builder.isFalse(fromPP.<Boolean> get("privateProject")),
                    builder.isTrue(fromPP.<Boolean> get("reportPublic"))));
            } else if (whichProjects == WhichProjects.ALLPROJECTS) {
                if (user.isAdmin()) {
                    // Always true
                    visibilityPredicates.add(builder.conjunction());
                }
            }

            return this;
        }

        /**
         * Adds plan states as criteria to the query.
         *
         * @param planStates
         *            the plan states to add
         * @return this query
         */
        public PlanQuery filterState(PlanState... planStates) {
            if (planStates.length == 0) {
                return this;
            }

            stateFilterPredicates.add(fromPP.<PlanState> get("state").in((Object[]) planStates));

            return this;
        }

        /**
         * Adds a minimum plan state to the query. Plans will have at least the
         * state provided.
         *
         * @param planState
         *            the plan state
         * @return this query
         */
        public PlanQuery filterMinState(PlanState planState) {
            PlanState[] planStates = new PlanState[PlanState.values().length - planState.ordinal()];

            int i = 0;
            for (PlanState p : PlanState.values()) {
                if (p.compareTo(planState) >= 0) {
                    planStates[i] = p;
                    i++;
                }
            }

            return filterState(planStates);
        }

        /**
         * Adds a filter for the plan name. The query matches plans with a name
         * like the filter string.
         *
         * If no filter was added to the query, plans with any name matches.
         *
         * @param filter
         *            the filter string
         * @return this query
         */
        public PlanQuery filterNameLike(String filter) {
            nameFilterPredicates.add(builder.like(fromPP.<String> get("name"), filter));
            return this;
        }

        /**
         * Adds a filter for the plan name. The query matches plans with a name
         * unlike the filter string.
         *
         * If no filter was added to the query, plans with any name matches.
         *
         * @param filter
         *            the filter string
         * @return this query
         */
        public PlanQuery filterNameUnlike(String filter) {
            nameFilterPredicates.add(builder.notLike(fromPP.<String> get("name"), filter));
            return this;
        }

        /**
         * Adds a filter to query only for plans with mapped measures.
         *
         * @return this query
         */
        public PlanQuery filterMapped() {
            Subquery<Integer> subquery = cq.subquery(Integer.class);
            Root<Leaf> fromTreeNode = subquery.from(Leaf.class);
            subquery.select(fromTreeNode.<Integer> get("id"));
            subquery.where(builder.and(
                fromTreeNode.<Measure> get("measure").isNotNull(),
                builder.equal(builder.function("rootNode", Integer.class, fromTreeNode.<Integer> get("id")), fromPlan
                    .<ObjectiveTree> get("tree").<TreeNode> get("root").<Integer> get("id"))));

            mappedFilterPredicate = builder.exists(subquery);
            return this;
        }

        /**
         * Adds a filter excluding all plans marked as playground.
         *
         * @return this query
         */
        public PlanQuery filterPlayground() {
            playgroundFilterPredicate = builder.isFalse(fromPP.<Boolean> get("playground"));
            return this;
        }

        /**
         * Finishes the query.
         */
        private void finishQuery() {
            List<Predicate> predicates = new ArrayList<Predicate>(5);

            // Where
            predicates.add(builder.or(visibilityPredicates.toArray(new Predicate[visibilityPredicates.size()])));
            if (stateFilterPredicates.size() > 0) {
                predicates.add(builder.or(stateFilterPredicates.toArray(new Predicate[stateFilterPredicates.size()])));
            }
            if (nameFilterPredicates.size() > 0) {
                predicates.add(builder.or(nameFilterPredicates.toArray(new Predicate[nameFilterPredicates.size()])));
            }
            if (mappedFilterPredicate != null) {
                predicates.add(mappedFilterPredicate);
            }
            if (playgroundFilterPredicate != null) {
                predicates.add(playgroundFilterPredicate);
            }
            cq.where(builder.and(predicates.toArray(new Predicate[predicates.size()])));

            // Order by
            cq.orderBy(builder.asc(fromPP.get("id")));
        }
    }

    /**
     * Constructs a new plan manager.
     */
    public PlanManager() {
        sessionPlans = new HashSet<Integer>();
    }

    /**
     * Unlocks all plans opened in this session.
     */
    public void unlockSessionPlans() {
        HashSet<Integer> lockedPlans = new HashSet<Integer>(sessionPlans);
        for (Integer planPropertiesId : lockedPlans) {
            unlockPlan(planPropertiesId);
        }
    }

    /**
     * Creates a new plan query.
     *
     * @return the plan query
     */
    public PlanQuery createQuery() {
        PlanQuery ps = new PlanQuery();
        ps.init();
        return ps;
    }

    /**
     * Returns all plans that fit the plan query.
     *
     * @param planQuery
     *            the plan query
     * @return the plans
     */
    public List<PlanProperties> list(PlanManager.PlanQuery planQuery) {
        planQuery.finishQuery();

        TypedQuery<PlanProperties> query = em.createQuery(planQuery.cq);
        List<PlanProperties> planProperties = query.getResultList();

        List<String> usernames = em
            .createQuery("SELECT u.username from User u WHERE u.userGroup = :userGroup", String.class)
            .setParameter("userGroup", user.getUserGroup()).getResultList();

        for (PlanProperties pp : planProperties) {

            // A plan may be edited:
            // user currently logged in is administrator
            // or user currently logged in is the owner
            // or user currently logged in is in the group of the owner
            boolean mayEdit = pp.isClosed()
                && (user.isAdmin() || user.getUsername().equals(pp.getOwner()) || usernames.contains(pp.getOwner()));

            pp.setMayEdit(mayEdit);
            pp.setAllowUnlock(pp.getOpenedByUser().equals(user.getUsername()) || user.isAdmin());
        }

        return planProperties;
    }

    /**
     * Reloads a plan. Checks if the provided plan is opened by the current
     * user.
     *
     * @param plan
     *            the plan to reload
     * @return the reloaded plan
     * @throws PlanningException
     *             if the plan could not be reloaded
     */
    public Plan reloadPlan(Plan plan) throws PlanningException {
        TypedQuery<Long> q = em
            .createQuery(
                "select count(pp.id) from  PlanProperties pp where (pp.openHandle = 1) and (pp.openedByUser = :user) and (pp.id = :propid)",
                Long.class);
        q.setParameter("user", user.getUsername());
        q.setParameter("propid", plan.getPlanProperties().getId());
        Long planCount = q.getSingleResult();
        if (planCount != 1) {
            throw new PlanningException("This plan has not been loaded before, reload is not possible.");
        }

        Plan reloadedPlan = em.find(Plan.class, plan.getId());
        this.initializePlan(reloadedPlan);
        log.info("Plan " + reloadedPlan.getPlanProperties().getName() + " reloaded!");
        return reloadedPlan;

    }

    /**
     * Loads the plan with the given plan-Id from the database. - without
     * locking the plan!
     *
     * @param planId
     *            the plan ID
     * @return the loaded plan
     */
    public Plan loadPlan(int planId) {
        Plan plan = em.find(Plan.class, planId);

        this.initializePlan(plan);
        return plan;
    }

    /**
     * Loads the given plan from the database and locks it if requested.
     *
     * @param propertyId
     *            the plan's PROPERTIES id!
     * @param readOnly
     *            states if the plan should be opened in read only mode
     * @return the loaded plan
     * @throws PlanningException
     *             if the plan could not be loaded
     */
    public Plan load(int propertyId, boolean readOnly) throws PlanningException {

        if (!readOnly) {
            // try to lock the plan
            Query q = em
                .createQuery("update PlanProperties pp set pp.openHandle = 1, pp.openedByUser = :user where (pp.openHandle is null or pp.openHandle = 0) and pp.id = :propid");
            q.setParameter("user", user.getUsername());
            q.setParameter("propid", propertyId);
            int num = q.executeUpdate();
            if (num < 1) {
                throw new PlanningException("The plan has been loaded by another user. Please choose another plan.");
            }
            // and add it to the list of loaded plans, so we can unlock it in
            // any case
            sessionPlans.add(propertyId);
        }
        // then load the plan
        Object result = em.createQuery("select p.id from Plan p where p.planProperties.id = " + propertyId)
            .getSingleResult();
        if (result != null) {
            Plan plan = loadPlan((Integer) result);
            plan.setReadOnly(readOnly);
            log.info("Plan {} : {} loaded.", propertyId, plan.getPlanProperties().getName());
            return plan;
        } else {
            throw new PlanningException("An unexpected error has occured while loading the plan.");
        }
    }

    /**
     * Hibernate initializes project and its parts.
     *
     * @param p
     *            the plan to initialize
     */
    private void initializePlan(Plan p) {
        Hibernate.initialize(p);
        Hibernate.initialize(p.getAlternativesDefinition());
        Hibernate.initialize(p.getSampleRecordsDefinition());
        Hibernate.initialize(p.getTree());
        initializeNodeRec(p.getTree().getRoot());
        log.debug("plan initialised");
    }

    /**
     * Traverses down the nodes in the tree and calls
     * <code>Hibernate.initialize</code> for each leaf. This is necessary to
     * provide the application with a convenient way of working with lazily
     * initialized collections or proxies.
     *
     * @param node
     *            node from where initialization shall start
     */
    private void initializeNodeRec(TreeNode node) {

        Hibernate.initialize(node);
        if (node.isLeaf()) {
            Leaf leaf = (Leaf) node;
            Transformer t = leaf.getTransformer();
            Hibernate.initialize(t);
            if (t instanceof OrdinalTransformer) {
                OrdinalTransformer nt = (OrdinalTransformer) t;
                Hibernate.initialize(nt.getMapping());
            }
            // log.debug("hibernate initialising Transformer: " +
            // leaf.getTransformer());
            for (Values value : leaf.getValueMap().values()) {
                Hibernate.initialize(value);
            }
        } else if (node instanceof Node) {
            Node recnode = (Node) node;
            Hibernate.initialize(node.getChildren());
            for (TreeNode newNode : recnode.getChildren()) {
                initializeNodeRec(newNode);
            }
        }
    }

    /**
     * Unlocks all plans in the database.
     */
    public void unlockAll() {
        this.unlockQuery(-1);
    }

    /**
     * Unlocks a plan with the provided plan properties ID.
     *
     * @param planPropertiesId
     *            the plan's PROPERTIES id
     */
    public void unlockPlan(int planPropertiesId) {
        // remove from list of locked plans
        sessionPlans.remove(planPropertiesId);

        unlockQuery(planPropertiesId);
    }

    /**
     * Unlocks plans in the database (dependent on parameter). If the pid is -1,
     * all plans are unlocked, otherwise the plan with the provided pid is
     * unlocked.
     *
     * @param pid
     *            The plan ID to unlock or -1 to unlock all plans
     */
    private void unlockQuery(long pid) {

        String where = "";
        if (pid > -1) {
            where = "where pp.id = " + pid;
        }

        Query q = em.createQuery("update PlanProperties pp set pp.openHandle = 0, pp.openedByUser = '' " + where);
        try {
            if (q.executeUpdate() < 1) {
                log.error("Unlocking plan of plans with with id [{}] failed.", pid);
            } else {
                log.info("Unlocked plans with id [{}].", pid);
            }
        } catch (Throwable e) {
            log.error("Unlocking plans with id [{}] failed:", pid, e);
        }
    }

    /**
     * Updates the state of the provided plan and saves the provided entity.
     *
     * @param plan
     *            the plan
     * @param currentState
     *            the state of the plan
     * @param entity
     *            the entity to save
     */
    public void save(Plan plan, PlanState currentState, Object entity) {

        log.debug("Persisting plan " + entity.getClass().getName());
       
        plan.getPlanProperties().setState(currentState);

        if (plan.getPlanProperties().getReportUpload().isDataExistent()) {
            plan.getPlanProperties().setReportUpload(new DigitalObject());

            String msg = "Please consider that because data underlying the preservation plan has been changed, the uploaded report was automatically removed. ";
            msg += "If you would like to make the updated report available, please generate it again and upload it in 'Plan Settings'.";
            facesMessages.addInfo(msg);
        }

        PlanProperties planProperties = em.merge(plan.getPlanProperties());
        em.persist(planProperties);
        plan.setPlanProperties(planProperties);

        em.persist(em.merge(entity));
    }

    // --------------- save operations for steps ---------------

    /**
     * Saves changes to the plan settings.
     *
     * @param planProperties
     *            the plan properties
     * @param alternativesDefinition
     *            alternatives to save
     */
    public void saveForPlanSettings(PlanProperties planProperties, AlternativesDefinition alternativesDefinition) {
        em.persist(em.merge(planProperties));
        em.persist(em.merge(alternativesDefinition));
    }

    // --------------- delete a plan from database ---------------

    /**
     * Method responsible for deleting a plan from database.
     *
     * @param plan
     *            the plan to delete.
     * @throws PlanningException
     *             if the plan could not be deleted
     */
    public void deletePlan(Plan plan) throws PlanningException {
        if (plan.isReadOnly()) {
            throw new PlanningException("Plans opened in read only mode cannot be deleted!");
        }
        log.info("Deleting plan {} with pid {}", plan.getPlanProperties().getName(), plan.getPlanProperties().getId());
        List<DigitalObject> digitalObjects = plan.getDigitalObjects();
        try {
            em.remove(em.merge(plan));
            em.flush();
            for (DigitalObject obj : digitalObjects) {
                bytestreamManager.delete(obj.getPid());
            }
        } catch (StorageException e) {
            throw e;
        } catch (Exception e) {
            throw new PlanningException("Failed to delete plan: " + plan.getPlanProperties().getName() + " with id: "
                + plan.getPlanProperties().getId(), e);
        }

    }

    // ********** getter/setter **********
    public WhichProjects getLastLoadMode() {
        return lastLoadMode;
    }

    public void setLastLoadMode(WhichProjects lastLoadMode) {
        this.lastLoadMode = lastLoadMode;
    }
}
TOP

Related Classes of eu.scape_project.planning.manager.PlanManager$PlanQuery

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.