com.flexive.war.beans.admin.main.WorkflowBean.java Source code

Java tutorial

Introduction

Here is the source code for com.flexive.war.beans.admin.main.WorkflowBean.java

Source

/***************************************************************
 *  This file is part of the [fleXive](R) backend application.
 *
 *  Copyright (c) 1999-2014
 *  UCS - unique computing solutions gmbh (http://www.ucs.at)
 *  All rights reserved
 *
 *  The [fleXive](R) backend application is free software; you can redistribute
 *  it and/or modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation;
 *  either version 2 of the License, or (at your option) any
 *  later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/licenses/gpl.html.
 *  A copy is found in the textfile GPL.txt and important notices to the
 *  license from the author are found in LICENSE.txt distributed with
 *  these libraries.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  For further information about UCS - unique computing solutions gmbh,
 *  please see the company website: http://www.ucs.at
 *
 *  For further information about [fleXive](R), please see the
 *  project website: http://www.flexive.org
 *
 *
 *  This copyright notice MUST APPEAR in all copies of the file!
 ***************************************************************/
package com.flexive.war.beans.admin.main;

import com.flexive.faces.FxJsfUtils;
import com.flexive.faces.messages.FxFacesMsgErr;
import com.flexive.faces.messages.FxFacesMsgInfo;
import com.flexive.faces.messages.FxFacesMsgWarn;
import com.flexive.shared.CacheAdmin;
import com.flexive.shared.EJBLookup;
import com.flexive.shared.FxSharedUtils;
import com.flexive.shared.exceptions.FxApplicationException;
import com.flexive.shared.structure.FxEnvironment;
import com.flexive.shared.workflow.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.faces.model.SelectItem;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Workflow service beans.
 *
 * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 * @version $Rev$
 */
public class WorkflowBean implements Serializable {
    private static final long serialVersionUID = 4771311329266251588L;

    private static final Log LOG = LogFactory.getLog(WorkflowBean.class);

    /**
     * Session key to store the last inserted step id (for validation purposes)
     */
    private static final String SESSION_LASTSTEPID = "__FXLASTNEWSTEPID__";

    private WorkflowHolder currentWorkflow = new WorkflowHolder();
    private static final String WORKFLOW_OVERVIEW = "workflowOverview";
    private static final String WORKFLOW_EDIT = "workflowEdit";

    private int overviewPageNumber;
    private int overviewRows;
    private String sortColumn;
    private String sortOrder;

    private static class WorkflowHolder {
        private WorkflowEdit workflow = null;
        private List<StepEdit> steps = null;
        private List<RouteEdit> routes = null;
        private long stepDefinitionId = -1;
        private long stepACL = -1;
        private long workflowId = -1;
        private int stepIndex = -1;
        private int routeIndex = -1;
        private long fromStepId = -1;
        private long toStepId = -1;
        private long userGroup = -1;

    }

    /**
     * @return true if the edit tab should be opened
     * @since 3.1.4
     */
    public boolean isOpenTab() {
        return currentWorkflow != null && currentWorkflow.workflowId >= 0;
    }

    /**
     * Opens the edit workflow in a tab
     * @return the name where to navigate
     * @since 3.1.4
     */
    public String openEditTab() {
        if (!isOpenTab())
            return edit();
        return WORKFLOW_EDIT;
    }

    public WorkflowHolder getCurrentWorkflow() {
        return currentWorkflow;
    }

    public void setCurrentWorkflow(WorkflowHolder currentWorkflow) {
        this.currentWorkflow = currentWorkflow;
    }

    /**
     * Navigate back to the overview and remembers the changes of the workflow
     *
     * @return overview page
     * @since 3.1.4
     */
    public String overview() {
        return WORKFLOW_OVERVIEW;
    }

    /**
     * Default constructor
     */
    public WorkflowBean() {
    }

    /**
     * Create a new workflow.
     *
     * @return the next page
     */
    public String create() {
        try {
            currentWorkflow.workflow.setId(EJBLookup.getWorkflowEngine().create(currentWorkflow.workflow));
            currentWorkflow.workflowId = currentWorkflow.workflow.getId();
            new FxFacesMsgInfo("Workflow.nfo.created", currentWorkflow.workflow.getName()).addToContext();
            return edit();
        } catch (Exception e) {
            new FxFacesMsgErr(e).addToContext();
            return "workflowCreate";
        }
        /*
           // test for cache transactions - keep until issue is resolved!
        *
          long workflowId = createTestWorkflow();
          try {
          ACL myWorkflowACL = EJBLookup.getAclInterface().load(EJBLookup.getAclInterface()
              .create("CACTUS_TEST_WORKFLOW", new FxString("Test workflow"),
              FxContext.get().getUserTicket().getMandatorId(),
                  "#000000", "cactus test ACL (ignore)", ACLCategory.WORKFLOW));
          EJBLookup.getAclInterface().assign(myWorkflowACL.getId(), Group.GROUP_EVERYONE,
                  true, true, true, true, true, true);
          WorkflowEdit editWorkflow = new WorkflowEdit(CacheAdmin.getEnvironment().getWorkflow(workflowId));
          // add two valid steps
          editWorkflow.getSteps().add(new StepEdit(new Step(-1, StepDefinition.EDIT_STEP_ID,
              editWorkflow.getId(), myWorkflowACL.getId())));
          editWorkflow.getSteps().add(new StepEdit(new Step(-2, StepDefinition.LIVE_STEP_ID,
              editWorkflow.getId(), myWorkflowACL.getId())));
          // ... and an invalid route (which will cause a rollback after the steps have been added)
          editWorkflow.getRoutes().add(new Route(-1, Group.GROUP_EVERYONE, -1, -10));
            
          List<Step> cachedSteps = CacheAdmin.getEnvironment().getSteps();
          try {
              EJBLookup.getWorkflowInterface().update(editWorkflow);
              assert false : "Should not be able to successfully create workflows with invalid routes.";
          } catch (Exception e) {
              if (!cachedSteps.equals(CacheAdmin.getEnvironment().getSteps())) {
                  FxJsfUtils.addErrorMsg("Steps should have been rollbacked.\nSteps before update: "
                  + cachedSteps.toString()
                  + "\nEnvironment: " + CacheAdmin.getEnvironment().getSteps(), null);
              }
          }
          } finally {
          EJBLookup.getWorkflowInterface().delete(workflowId);
          }
          return "workflowOverview";*/
    }

    /**
     * Deletes the workflow set in workflowId.
     *
     * @return the next page
     */
    public String delete() {
        try {
            long workflowId = currentWorkflow.workflowId;
            if (workflowId == -1) {
                new FxFacesMsgErr("Workflow.err.notSelected").addToContext();
            } else {
                String oldName = CacheAdmin.getEnvironment().getWorkflow(workflowId).getName();
                EJBLookup.getWorkflowEngine().remove(workflowId);
                new FxFacesMsgInfo("Workflow.nfo.deleted", oldName).addToContext();
            }
        } catch (Exception e) {
            new FxFacesMsgErr(e).addToContext();
        }
        return WORKFLOW_OVERVIEW;
    }

    /**
     * Edit the workflow set in workflowId.
     *
     * @return the next page
     */
    public String edit() {
        long workflowId = currentWorkflow.workflowId;
        if (workflowId == -1) {
            new FxFacesMsgErr("Workflow.err.notSelected").addToContext();
        } else {
            WorkflowEdit workflow = new WorkflowEdit(CacheAdmin.getEnvironment().getWorkflow(workflowId));
            currentWorkflow.workflow = workflow;
            getSteps().clear();
            for (Step step : workflow.getSteps()) {
                getSteps().add(new StepEdit(step));
            }
            getRoutes().clear();
            for (Route route : workflow.getRoutes()) {
                getRoutes().add(new RouteEdit(route));
            }
        }
        return WORKFLOW_EDIT;
    }

    /**
     * Save the current workflow.
     *
     * @return the next page
     */
    public String save() {
        try {
            WorkflowEdit workflow = currentWorkflow.workflow;
            workflow.setSteps(currentWorkflow.steps);
            workflow.setRoutes(currentWorkflow.routes);
            EJBLookup.getWorkflowEngine().update(workflow);
            currentWorkflow.workflow = workflow;
            new FxFacesMsgInfo("Workflow.nfo.updated", workflow.getName()).addToContext();
            return null;
        } catch (Exception e) {
            new FxFacesMsgErr(e).addToContext();
            return null;
        }
    }

    /**
     * Add a new step definition to the current workflow.
     */
    public synchronized void addStep() {
        StepEdit step = new StepEdit(new Step(getNewStepId(), currentWorkflow.stepDefinitionId,
                currentWorkflow.workflow.getId(), currentWorkflow.stepACL));
        getSteps().add(step);
        StepDefinition sd = CacheAdmin.getFilteredEnvironment().getStepDefinition(currentWorkflow.stepDefinitionId);
        if (sd.isUnique()) {
            //make sure the unique target is added as well
            boolean found = false;
            for (StepEdit se : getSteps()) {
                if (se.getStepDefinitionId() == sd.getUniqueTargetId()) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                //add step definiton
                getSteps().add(new StepEdit(new Step(getNewStepId(), sd.getUniqueTargetId(),
                        currentWorkflow.workflow.getId(), currentWorkflow.stepACL)));
            }
        }
        currentWorkflow.workflow.setSteps(currentWorkflow.steps);
    }

    /**
     * Remove the step at position "stepIndex".
     *
     * @return the next page
     */
    public void removeStep() {
        int stepIndex = currentWorkflow.stepIndex;
        if (stepIndex == -1) {
            new FxFacesMsgErr("Workflow.err.step.notSelected").addToContext();
        } else if (stepIndex < 0 || stepIndex > getSteps().size()) {
            new FxFacesMsgErr("Workflow.err.step.invalidIndex",
                    FxJsfUtils.getLocalizedMessage("Workflow.err.step.invalidIndex.detail", stepIndex))
                            .addToContext();
        } else if (getUsedSteps().contains(getSteps().get(stepIndex))) {
            // TODO add shortcut method
            String stepLabel = CacheAdmin.getEnvironment()
                    .getStepDefinition(getSteps().get(stepIndex).getStepDefinitionId()).getLabel()
                    .getBestTranslation();
            new FxFacesMsgErr("Workflow.err.step.inUse", stepLabel).addToContext();
        } else {
            getSteps().remove(stepIndex);
        }
        currentWorkflow.workflow.setSteps(getSteps());
    }

    public void moveStepUp() {
        moveStep(currentWorkflow.stepIndex - 1);
    }

    public void moveStepDown() {
        moveStep(currentWorkflow.stepIndex + 1);
    }

    private void moveStep(int targetPosition) {
        final StepEdit step = getSteps().remove(currentWorkflow.stepIndex);
        getSteps().add(Math.max(0, Math.min(getSteps().size(), targetPosition)), step);
    }

    /**
     * Check if a step can be removed as mapped function
     *
     * @return if step can be removed
     */
    public Map<Step, Boolean> getCanRemoveStep() {
        return FxSharedUtils.getMappedFunction(new FxSharedUtils.ParameterMapper<Step, Boolean>() {
            @Override
            public Boolean get(Object key) {
                if (key == null) {
                    return null;
                }
                Step s = (Step) key;
                for (Route r : getRoutes())
                    if (r.getFromStepId() == s.getId() || r.getToStepId() == s.getId())
                        return false;
                for (Step _s : getSteps())
                    if (CacheAdmin.getEnvironment().getStepDefinition(_s.getStepDefinitionId())
                            .getUniqueTargetId() == s.getStepDefinitionId())
                        return false;
                return true;
            }
        }, true);
    }

    /**
     * Add a new route between steps[fromStepIndex] and steps[toStepIndex].
     */
    public void addRoute() {
        long fromStepId = currentWorkflow.fromStepId;
        long toStepId = currentWorkflow.toStepId;
        if (getStep(fromStepId) == null || getStep(toStepId) == null) {
            new FxFacesMsgErr("Workflow.err.route.create.steps.notFound").addToContext();
            return;
        }
        if (fromStepId == toStepId) {
            new FxFacesMsgErr("Workflow.err.route.create.steps.identical").addToContext();
            return;
        }
        long userGroup = currentWorkflow.userGroup;
        RouteEdit route = new RouteEdit(new Route(-1, userGroup, fromStepId, toStepId));
        List<RouteEdit> routes = currentWorkflow.routes;
        if (routes.contains(route)) {
            new FxFacesMsgWarn("Workflow.wng.route.exists").addToContext();
        } else {
            routes.add(route);
            final FxEnvironment env = CacheAdmin.getEnvironment();
            String fromStepName = env.getStepDefinition(getStep(route.getFromStepId()).getStepDefinitionId())
                    .getLabel().getBestTranslation();
            String toStepName = env.getStepDefinition(getStep(route.getToStepId()).getStepDefinitionId()).getLabel()
                    .getBestTranslation();
            String groupName = String.valueOf(userGroup);
            try {
                groupName = EJBLookup.getUserGroupEngine().load(userGroup).getName();
            } catch (FxApplicationException e) {
                LOG.error(e);
            }
            new FxFacesMsgInfo("Workflow.nfo.route.added", fromStepName, toStepName, groupName).addToContext();
        }
    }

    /**
     * Remove an existing route (by index).
     */
    public void removeRoute() {
        int routeIndex = currentWorkflow.routeIndex;
        if (routeIndex < 0 || routeIndex > getRoutes().size()) {
            new FxFacesMsgErr("Workflow.err.route.remove.invalid", routeIndex).addToContext();
            return;
        }
        List<RouteEdit> routes = currentWorkflow.routes;
        Route route = routes.get(routeIndex);
        // TODO add convenience methods for route --> step --> stepdefinitionid --> stepdefinition
        final FxEnvironment env = CacheAdmin.getEnvironment();
        String fromStepName = env.getStepDefinition(getStep(route.getFromStepId()).getStepDefinitionId()).getLabel()
                .getBestTranslation();
        String toStepName = env.getStepDefinition(getStep(route.getToStepId()).getStepDefinitionId()).getLabel()
                .getBestTranslation();
        routes.remove(routeIndex);
        String grp = "" + route.getGroupId();
        try {
            grp = EJBLookup.getUserGroupEngine().load(route.getGroupId()).getName();
        } catch (FxApplicationException e) {
            //ignore
        }
        new FxFacesMsgInfo("Workflow.nfo.route.removed", fromStepName, toStepName, grp).addToContext();
    }

    /**
     * Return a list of all workflow step definitions that can be added to the current workflow
     * (i.e. all defined steps minus the steps already used in the workflow).
     *
     * @return a list of all workflow step definitions that can be added to the current workflow
     */
    @SuppressWarnings("unchecked")
    public List<SelectItem> getStepsForAdding() {
        final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>(
                CacheAdmin.getFilteredEnvironment().getStepDefinitions());
        List<StepEdit> steps = currentWorkflow.steps;
        stepDefinitions.removeAll(FxSharedUtils.getUsedStepDefinitions(
                steps != null ? steps : currentWorkflow.workflow.getSteps(), stepDefinitions));
        return FxJsfUtils.asSelectListWithLabel(stepDefinitions);
    }

    /**
     * Return a list of all workflow steps that are available for routes.
     *
     * @return a list of all workflow steps that are available for routes.
     */
    public List<SelectItem> getStepsForRoutes() {
        List<SelectItem> result = new ArrayList<SelectItem>();
        List<StepEdit> steps = currentWorkflow.steps;
        List<? extends Step> listSteps = steps != null ? steps : CacheAdmin.getFilteredEnvironment().getSteps();
        if (steps == null) {
            // TODO ugly validation fix to allow temporary steps (negative id)
            Object lastNewStepIdValue = FxJsfUtils.getSessionAttribute(SESSION_LASTSTEPID);
            long lastNewStepId = lastNewStepIdValue != null ? (Long) lastNewStepIdValue : -1;
            for (long i = -1; i >= lastNewStepId; i--) {
                result.add(new SelectItem(i, ""));
            }
        }

        for (Step step : listSteps) {
            StepDefinition stepDefinition = CacheAdmin.getEnvironment()
                    .getStepDefinition(step.getStepDefinitionId());
            result.add(new SelectItem(step.getId(), stepDefinition.getLabel().getBestTranslation()));
        }
        return result;
    }

    /**
     * Returns a negative pseudo step ID to be used for identifying steps
     * in routes before they are persisted to the database.
     *
     * @return a new internal step ID to be used for identifying steps
     */
    private long getNewStepId() {
        long minStepId = -1;
        for (Step step : getSteps()) {
            if (step.getId() <= minStepId) {
                minStepId = step.getId() - 1;
            }
        }
        FxJsfUtils.setSessionAttribute(SESSION_LASTSTEPID, minStepId);
        return minStepId;
    }

    /**
     * Returns a list of all steps used by the current route definitions.
     *
     * @return a list of all steps used by the current route definitions.
     */
    private List<StepEdit> getUsedSteps() {
        List<StepEdit> routeSteps = new ArrayList<StepEdit>();
        for (Route route : getRoutes()) {
            routeSteps.add(getStep(route.getFromStepId()));
            routeSteps.add(getStep(route.getToStepId()));
        }
        return routeSteps;
    }

    /**
     * Return the step with the given ID (may be negative for temporary
     * steps).
     *
     * @param id step id
     * @return the step with the requested ID
     */
    private StepEdit getStep(long id) {
        for (StepEdit step : getSteps()) {
            if (step.getId() == id) {
                return step;
            }
        }
        return null;
    }

    public List<Workflow> getList() {
        return CacheAdmin.getFilteredEnvironment().getWorkflows();
    }

    /**
     * Return the current workflow.
     *
     * @return the current workflow.
     */
    public WorkflowEdit getWorkflow() {
        if (currentWorkflow.workflow != null) {
            return currentWorkflow.workflow;
        }
        currentWorkflow.workflow = new WorkflowEdit();
        return currentWorkflow.workflow;
    }

    public void setWorkflow(WorkflowEdit workflow) {
        this.currentWorkflow.workflow = workflow;
    }

    public long getStepDefinitionId() {
        return currentWorkflow.stepDefinitionId;
    }

    public void setStepDefinitionId(long stepDefinitionId) {
        this.currentWorkflow.stepDefinitionId = stepDefinitionId;
    }

    public long getStepACL() {
        return currentWorkflow.stepACL;
    }

    public void setStepACL(long stepACL) {
        this.currentWorkflow.stepACL = stepACL;
    }

    public String getSortColumn() {
        return sortColumn;
    }

    public void setSortColumn(String sortColumn) {
        this.sortColumn = sortColumn;
    }

    public String getSortOrder() {
        return sortOrder;
    }

    public void setSortOrder(String sortOrder) {
        this.sortOrder = sortOrder;
    }

    /**
     * Return the current step table.
     *
     * @return the current step table.
     */
    public List<StepEdit> getSteps() {
        if (currentWorkflow.steps != null) {
            return currentWorkflow.steps;
        }
        currentWorkflow.steps = new ArrayList<StepEdit>();
        return currentWorkflow.steps;
    }

    public void setSteps(List<StepEdit> steps) {
        this.currentWorkflow.steps = steps;
    }

    /**
     * Return a map with all steps indexed by ID.
     *
     * @return a map with all steps indexed by ID.
     */
    public Map<Long, StepEdit> getStepsById() {
        HashMap<Long, StepEdit> result = new HashMap<Long, StepEdit>(currentWorkflow.steps.size());
        for (StepEdit step : getSteps()) {
            result.put(step.getId(), step);
        }
        return result;
    }

    /**
     * Return the current route table.
     *
     * @return the current route table.
     */
    public List<RouteEdit> getRoutes() {
        if (currentWorkflow.routes != null) {
            return currentWorkflow.routes;
        }
        currentWorkflow.routes = new ArrayList<RouteEdit>();
        return currentWorkflow.routes;
    }

    public void setRoutes(List<RouteEdit> routes) {
        this.currentWorkflow.routes = routes;
    }

    public long getWorkflowId() {
        return currentWorkflow.workflowId;
    }

    public void setWorkflowId(long workflowId) {
        this.currentWorkflow.workflowId = workflowId;
    }

    public int getStepIndex() {
        return currentWorkflow.stepIndex;
    }

    public void setStepIndex(int stepIndex) {
        this.currentWorkflow.stepIndex = stepIndex;
    }

    public int getRouteIndex() {
        return currentWorkflow.routeIndex;
    }

    public void setRouteIndex(int routeIndex) {
        this.currentWorkflow.routeIndex = routeIndex;
    }

    public long getFromStepId() {
        return currentWorkflow.fromStepId;
    }

    public void setFromStepId(long fromStepId) {
        this.currentWorkflow.fromStepId = fromStepId;
    }

    public long getToStepId() {
        return currentWorkflow.toStepId;
    }

    public void setToStepId(long toStepId) {
        this.currentWorkflow.toStepId = toStepId;
    }

    public long getUserGroup() {
        return currentWorkflow.userGroup;
    }

    public void setUserGroup(long userGroup) {
        this.currentWorkflow.userGroup = userGroup;
    }

    public int getOverviewPageNumber() {
        return overviewPageNumber;
    }

    public void setOverviewPageNumber(int overviewPageNumber) {
        this.overviewPageNumber = overviewPageNumber;
    }

    public int getOverviewRows() {
        return overviewRows;
    }

    public void setOverviewRows(int overviewRows) {
        this.overviewRows = overviewRows;
    }
}