eu.planets_project.pp.plato.action.project.LoadPlanAction.java Source code

Java tutorial

Introduction

Here is the source code for eu.planets_project.pp.plato.action.project.LoadPlanAction.java

Source

/*******************************************************************************
 * Copyright (c) 2006-2010 Vienna University of Technology, 
 * Department of Software Technology and Interactive Systems
 *
 * All rights reserved. This program and the accompanying
 * materials are made available under the terms of the
 * Apache License, Version 2.0 which accompanies
 * this distribution, and is available at
 * http://www.apache.org/licenses/LICENSE-2.0 
 *******************************************************************************/
package eu.planets_project.pp.plato.action.project;

import java.io.Serializable;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.List;

import javax.annotation.PreDestroy;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.hibernate.Hibernate;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.RaiseEvent;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.datamodel.DataModelSelection;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.faces.FacesMessages;

import eu.planets_project.pp.plato.action.TestDataLoader;
import eu.planets_project.pp.plato.action.interfaces.IAnalyseResults;
import eu.planets_project.pp.plato.action.interfaces.ICreateExecutablePlan;
import eu.planets_project.pp.plato.action.interfaces.IDefineAlternatives;
import eu.planets_project.pp.plato.action.interfaces.IDefineBasis;
import eu.planets_project.pp.plato.action.interfaces.IDefinePlan;
import eu.planets_project.pp.plato.action.interfaces.IDefineSampleRecords;
import eu.planets_project.pp.plato.action.interfaces.IDevelopExperiments;
import eu.planets_project.pp.plato.action.interfaces.IEvaluateExperiments;
import eu.planets_project.pp.plato.action.interfaces.IFastTrackAnalyseResults;
import eu.planets_project.pp.plato.action.interfaces.IFastTrackDefineRequirements;
import eu.planets_project.pp.plato.action.interfaces.IFastTrackEvaluateAlternatives;
import eu.planets_project.pp.plato.action.interfaces.IGoNoGo;
import eu.planets_project.pp.plato.action.interfaces.IIdentifyRequirements;
import eu.planets_project.pp.plato.action.interfaces.IRunExperiments;
import eu.planets_project.pp.plato.action.interfaces.ISetImportanceFactorsAction;
import eu.planets_project.pp.plato.action.interfaces.ITransformMeasuredValues;
import eu.planets_project.pp.plato.action.interfaces.IUtilAction;
import eu.planets_project.pp.plato.action.interfaces.IValidatePlan;
import eu.planets_project.pp.plato.model.Plan;
import eu.planets_project.pp.plato.model.PlanProperties;
import eu.planets_project.pp.plato.model.PlanState;
import eu.planets_project.pp.plato.model.User;
import eu.planets_project.pp.plato.model.Values;
import eu.planets_project.pp.plato.model.transform.OrdinalTransformer;
import eu.planets_project.pp.plato.model.transform.Transformer;
import eu.planets_project.pp.plato.model.tree.Leaf;
import eu.planets_project.pp.plato.model.tree.Node;
import eu.planets_project.pp.plato.model.tree.TreeNode;
import eu.planets_project.pp.plato.util.IDownloadManagerHelperBean;
import eu.planets_project.pp.plato.util.PlatoLogger;

/**
 * Loads PPs
 *
 * Implements operations specified in {@link eu.planets_project.pp.plato.action.interfaces.LoadPlanAction}
 *
 * @author Hannes Kulovits
 */

@Name("loadPlan")
@Scope(ScopeType.SESSION)
public class LoadPlanAction implements Serializable {

    private static final long serialVersionUID = -5699231548828633148L;

    private enum WhichProjects {
        ALLPROJECTS, PUBLICPROJECTS, MYPROJECTS, FTEPROJECTS, PUBLICFTEPROJECTS;
    }

    private static final Log log = PlatoLogger.getLogger(LoadPlanAction.class);

    @In(required = false, create = true)
    @Out(scope = ScopeType.APPLICATION)
    private IUtilAction utilAction;

    @In
    private FacesContext facesContext;

    /**
     * This download manager <b>helper</b> bean is used in the DownloadServlet because
     * injecting or looking up the EntityManager in a http servlet is just pain.
     *
     * @see eu.planets_project.pp.plato.util.DownloadServlet
     */
    @In(create = true)
    @Out(required = true)
    private IDownloadManagerHelperBean downloadManagerHelperBean;

    /**
     * we also have to inject the selected project because of our newProject
     * observer. When a new project is created, selectedPlan must be injected
     * into LoadPlanAction. If we would not inject it, selectedPlan would
     * be outjected as null.
     */
    @In(required = false)
    @Out(required = false, scope = ScopeType.SESSION)
    private Plan selectedPlan;

    /**
     * Contains projects from database.
     */
    @DataModel
    private List<PlanProperties> projectList;

    /**
     * Entity manager for persistence.
     */
    @In
    EntityManager em;

    public void setEm(EntityManager em) {
        this.em = em;
    }

    /**
     * Plan selected by user from list {@link #projectList}.
     */
    @DataModelSelection(value = "projectList")
    private PlanProperties selection;

    /**
     * if the session ends, open projects must be closed but it is not sure that
     * the selectedPlan is still in the session context therefore keep the id
     * of the planProperties separately
     */
    private int planPropertiesId = 0;

    @In(create = true)
    private TestDataLoader testDataLoader;

    private WhichProjects lastLoadMode = WhichProjects.MYPROJECTS;

    public WhichProjects getLastLoadMode() {
        return lastLoadMode;
    }

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

    @Observer("projectListChanged")
    public String relist() {
        list(lastLoadMode);
        log.debug("reloading  in " + lastLoadMode + ": number of projects loaded: " + projectList.size());
        return "success";
    }

    @Transactional
    @PreDestroy
    public void preDestroy() {
        // maybe there is an open project left - close it
        unlockProject();
    }

    @Destroy
    public void destroy() {
    }

    /**
     * Initializes the user and loads the project from database when session
     * component is created.
     * This is called when this bean is created.
     */
    @Create
    public void onCreate() {
        listMyProjects();
    }

    public String listFTEProjects() {
        list(WhichProjects.FTEPROJECTS);
        return "success";
    }

    public String listAllProjects() {
        list(WhichProjects.ALLPROJECTS);
        return "success";
    }

    public String listMyProjects() {
        list(WhichProjects.MYPROJECTS);
        return "success";
    }

    public String listPublicProjects() {
        list(WhichProjects.PUBLICPROJECTS);
        return "success";
    }

    public String listPublicFTEResults() {
        list(WhichProjects.PUBLICFTEPROJECTS);
        return "success";
    }

    @Out
    private String planlist = "my plans";

    /**
     * Load projects from data:
     * <ul>
     *   <li>When administrator is logged in, all projects are loaded from database.</li>
     *   <li>For any other user, only projects are loaded that are <b>not</b> set to private by another user.</li>
     * </ul>
     *
     * Furthermore, checks if project is locked by the current user, who may thus be allowed to unlock the project.
     * In this case the {@link PlanProperties#isAllowReload()} is set. In the user interface this flag means
     * that an 'Unlock' button is displayed.
     */
    public void list(WhichProjects whichProjects) {

        Contexts.getSessionContext().remove("projectList");

        // changed flag needs to be initialized here because list is the first method that is called
        // when Plato is accessed.
        // Injecting it as a member variable with create=true didn't work for some reason.
        String changed = (String) Contexts.getSessionContext().get("changed");
        if (changed == null)
            Contexts.getSessionContext().set("changed", "");

        String projectListQuery;

        if (whichProjects == WhichProjects.MYPROJECTS) {
            // load user's projects
            //projectListQuery = "select p from PlanProperties p where (p.owner = '"+ user.getUsername() + "' and not (p.projectBasis.identificationCode LIKE 'FAST-TRACK-%'))" + " order by p.id" ;
            projectListQuery = "select p.planProperties from Plan p where" + " (p.planProperties.owner = '"
                    + user.getUsername() + "')"
                    + " and (p.projectBasis.identificationCode = null or p.projectBasis.identificationCode NOT LIKE 'FAST-TRACK-%')"
                    + " order by p.planProperties.id";

            setPlanlist("my preservation plans");
        } else if (whichProjects == WhichProjects.ALLPROJECTS && (user.isAdmin())) {
            // load all projects, public and private, 
            // but ONLY if the user is an admin
            projectListQuery = "select p from PlanProperties p order by p.id";
            setPlanlist("all preservation plans");
        } else if (whichProjects == WhichProjects.FTEPROJECTS) {

            projectListQuery = "select p.planProperties from Plan p where" + " (p.planProperties.owner = '"
                    + user.getUsername() + "')" + " and (p.projectBasis.identificationCode LIKE 'FAST-TRACK-%')"
                    + " order by p.planProperties.id";

            setPlanlist("fast track plans");
        } else if (whichProjects == WhichProjects.PUBLICFTEPROJECTS) {

            projectListQuery = "select p.planProperties from Plan p where"
                    + " (p.planProperties.privateProject = false )"
                    + " and (p.projectBasis.identificationCode LIKE 'FAST-TRACK-%')"
                    + " order by p.planProperties.id";

            setPlanlist("public fast track plans");

        } else {
            // load all public projects, which includes those with published reports
            projectListQuery = "select p.planProperties from Plan p where ((p.planProperties.privateProject = false)"
                    + " or (p.planProperties.privateProject = true and p.planProperties.reportPublic = true)) and p.projectBasis.identificationCode NOT LIKE 'FAST-TRACK-%' "
                    + " order by p.planProperties.id";
            setPlanlist("public preservation plans");
        }

        projectList = em.createQuery(projectListQuery).getResultList();
        try {
            // if there are no PUBLIC projects, we want to load them:
            if (projectList.size() == 0
                    && (whichProjects != WhichProjects.MYPROJECTS && whichProjects != WhichProjects.FTEPROJECTS)) {
                testDataLoader.importAutoloadPlans();
                projectList = em.createQuery(projectListQuery).getResultList();
            }
        } catch (Exception e) {
            FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "Failed to insert testdata.");
            log.fatal("Testdataloader failed.", e);
        }

        //
        // readOnly in PlanProperties is *transient*, it is used in loadPlan.xhtml
        // to determine if a user is allowed to load a project
        //
        for (PlanProperties pp : projectList) {

            //
            // a project may not be loaded when
            //   ... it is set to private
            //   ... AND the user currently logged in is not the administrator
            //   ... AND the user currently logged in is not the owner of that project
            boolean readOnly = pp.isPrivateProject() && !user.isAdmin()
                    && !user.getUsername().equals(pp.getOwner());

            boolean allowReload = pp.getOpenedByUser().equals(user.getUsername()) && selectedPlan == null;

            pp.setReadOnly(readOnly);
            pp.setAllowReload(allowReload);
        }
        setLastLoadMode(whichProjects);
    }

    public void setPlanlist(String planlist) {
        this.planlist = planlist;
    }

    @In(create = true)
    ProjectSettings projectSettings;

    @In(create = true)
    IFastTrackDefineRequirements FTrequirements;

    @In(create = true)
    IFastTrackEvaluateAlternatives FTevaluate;

    @In(create = true)
    IFastTrackAnalyseResults FTanalyse;

    @In(create = true)
    IDefineBasis defineBasis;

    @In(create = true)
    IDefineSampleRecords defineSampleRecords;

    @In(create = true)
    IIdentifyRequirements identifyRequirements;

    @In(create = true)
    IDefineAlternatives defineAlternatives;

    @In(create = true)
    IGoNoGo gonogo;

    @In(create = true)
    IDevelopExperiments devexperiments;

    @In(create = true)
    IRunExperiments runexperiments;

    @In(create = true)
    IEvaluateExperiments evalexperiments;

    @In(create = true)
    ITransformMeasuredValues transform;

    @In(create = true)
    ISetImportanceFactorsAction importanceFactors;

    @In(create = true)
    IAnalyseResults analyseResults;

    @In(create = true)
    ICreateExecutablePlan createExecutablePlan;

    @In(create = true)
    IDefinePlan definePlan;

    @In(create = true)
    IValidatePlan validatePlan;

    @In
    private User user;

    public void setPlanPropertiesID(int id) {
        planPropertiesId = id;
    }

    /**
     * Loads a project from database that was selected by the user.
     *
     * Previously loaded project is unlocked.
     *
     * Forwards the user to respective workflowstep by calling the workflow step's
     * <code>enter</code> method.
     */
    @RaiseEvent("reload")
    public String load() {
        String id = "";
        try {
            id = ((HttpServletRequest) facesContext.getExternalContext().getRequest()).getSession().getId();
        } catch (RuntimeException e) {
            log.debug("Couldn't get SessionID");
        }
        log.info("Session=" + id + " of user " + user.getUsername() + " is loading project " + selection.getId()
                + " - " + selection.getName());

        // try to lock the project
        Query q = em.createQuery(
                "update PlanProperties pp set pp.openHandle = 1, pp.openedByUser = '" + user.getUsername()
                        + "' where (pp.openHandle is null or pp.openHandle = 0) and pp.id = " + selection.getId());
        int num = q.executeUpdate();
        if (num < 1) {
            FacesMessages.instance().add(FacesMessage.SEVERITY_INFO,
                    "In the meantime the plan has been loaded by an other user. Please choose another plan.");
            relist();
            log.debug("Locking plan failed");
            return null;
        }
        List<Plan> list = em.createQuery("select p from Plan p where p.planProperties.id = " + selection.getId())
                .getResultList();

        // we locked the project before be we now cannot load it. not good.
        if (list.size() != 1) {
            FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
                    "An unexpected error has occured while loading the plan.");

            return null;
        }
        // ok - the selected project is free - unlock the old project
        unlockProject();
        // load the selected project (and keep the id!)
        setPlanPropertiesID(selection.getId());
        selectedPlan = em.find(Plan.class, list.get(0).getId());

        // Strangely enough the outjection doesnt work here, so to be sure we set the member explicitly
        Contexts.getSessionContext().set("selectedPlan", selectedPlan);

        this.initializeProject(selectedPlan);
        log.info("Plan " + selectedPlan.getPlanProperties().getName() + " loaded!");

        String msg = "The plan you loaded has reached the state " + selectedPlan.getState().getStateName()
                + ". Therefore you have been directed to the subsequent workflow step.";
        FacesMessages.instance().add(FacesMessage.SEVERITY_INFO, msg);

        if (selectedPlan.isFastTrackEvaluationPlan()) {

            switch (selectedPlan.getState().getValue()) {
            case PlanState.FTE_INITIALISED:
                return FTrequirements.enter();
            case PlanState.FTE_REQUIREMENTS_DEFINED:
                return FTevaluate.enter();
            case PlanState.FTE_ALTERNATIVES_EVALUATED:
                return FTanalyse.enter();
            case PlanState.FTE_RESULTS_ANALYSED:
                return FTanalyse.enter();
            }
        }

        // redirect to step corresponding to the project's state:
        switch (selectedPlan.getState().getValue()) {
        case PlanState.BASIS_DEFINED:
            return defineSampleRecords.enter();
        case PlanState.RECORDS_CHOSEN:
            return identifyRequirements.enter();
        case PlanState.TREE_DEFINED:
            return defineAlternatives.enter();
        case PlanState.ALTERNATIVES_DEFINED:
            return gonogo.enter();
        case PlanState.GO_CHOSEN:
            return devexperiments.enter();
        case PlanState.EXPERIMENT_DEFINED:
            return runexperiments.enter();
        case PlanState.EXPERIMENT_PERFORMED:
            return evalexperiments.enter();
        case PlanState.RESULTS_CAPTURED:
            return transform.enter();
        case PlanState.TRANSFORMATION_DEFINED:
            return importanceFactors.enter();
        case PlanState.WEIGHTS_SET:
            return analyseResults.enter();
        case PlanState.ANALYSED:
            return createExecutablePlan.enter();
        case PlanState.EXECUTEABLE_PLAN_CREATED:
            return definePlan.enter();
        case PlanState.PLAN_DEFINED:
            return validatePlan.enter();
        case PlanState.PLAN_VALIDATED:
            return validatePlan.enter();
        default:
            return defineBasis.enter();
        }
    }

    public String startFastTrackEvaluation() {
        unlockProject();

        selectedPlan = new Plan();
        selectedPlan.getPlanProperties().setAuthor(user.getFullName());
        selectedPlan.getPlanProperties().setPrivateProject(true);
        selectedPlan.getPlanProperties().setOwner(user.getUsername());

        // set Fast Track properties
        selectedPlan.getState().setValue(PlanState.FTE_INITIALISED);
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-kkmmss");
        String timestamp = format.format(new Date(System.currentTimeMillis()));
        String identificationCode = Plan.fastTrackEvaluationPrefix + timestamp;
        selectedPlan.getProjectBasis().setIdentificationCode(identificationCode);

        // We have to prevent the user from navigating to the step 'Load plan'
        // because the user wouldn't be able to leave this step: Going to 'Define
        // Basis' is not possible as the project hasn't been saved so far.
        //
        // We 'activate' the changed flag so that the user is asked to either
        // save the project or discard changes.
        TreeNode root = new Node();
        root.setName("Root");
        selectedPlan.getTree().setRoot(root);

        // ok - the selected project is free - unlock the old project
        unlockProject();

        // load the selected project (and keep the id!)
        setPlanPropertiesID(selectedPlan.getPlanProperties().getId());

        // Strangely enough the outjection doesnt work here, so to be sure we set the member explicitly
        Contexts.getSessionContext().set("selectedPlan", selectedPlan);

        this.initializeProject(selectedPlan);

        FTrequirements.enter();

        return "success";
    }

    /**
     * Hibernate initializes project and its parts.
     */
    public void initializeProject(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 projects in database.
     */
    public void unlockAll() {
        this.unlockQuery(false);
    }

    /**
     * Unlocks certain projects in database (dependent on parameter)
     *
     * @param useId If this is true, only project with id {@link #planPropertiesId} will be unlocked;
     * otherwise, all projects in database will be unlocked
     */
    private void unlockQuery(boolean useId) {

        String where = "";
        if (useId) {
            where = "where pp.id = " + planPropertiesId;
        }

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

        planPropertiesId = 0;
    }

    /**
     * Unlocks project with id {@link #planPropertiesId}.
     */
    public void unlockProject() {
        if (planPropertiesId != 0) {
            log.info("unlocking plan " + planPropertiesId);
            utilAction.unlockPlan(planPropertiesId);
        }
    }

    /**
     * Unlocks project depending on data model selection {@link #selection}
     */
    public String unlockselectedPlan() {
        if (selection != null) {
            planPropertiesId = selection.getId();
            unlockQuery(true);
        }
        return relist();
    }

    /**
     * Closes currently selected project {@link #selectedPlan}
     */
    public String closeProject() {
        if (selectedPlan != null) {
            unlockProject();
            selectedPlan = null;
        } else {
            log.debug("not unlocking any plan because none is open.");
        }
        return "success";
    }
}