module.workflow.presentationTier.actions.ProcessManagement.java Source code

Java tutorial

Introduction

Here is the source code for module.workflow.presentationTier.actions.ProcessManagement.java

Source

/*
 * @(#)ProcessManagement.java
 *
 * Copyright 2009 Instituto Superior Tecnico
 * Founding Authors: Luis Cruz, Paulo Abrantes
 * 
 *      https://fenix-ashes.ist.utl.pt/
 * 
 *   This file is part of the Case Handleing Based Workflow Module.
 *
 *   The Case Handleing Based Workflow Module is free software: you can
 *   redistribute it and/or modify it under the terms of the GNU Lesser General
 *   Public License as published by the Free Software Foundation, either version 
 *   3 of the License, or (at your option) any later version.
 *
 *   The Workflow Module 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 Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with the Workflow Module. If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package module.workflow.presentationTier.actions;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jvstm.cps.ConsistencyException;
import module.workflow.activities.ActivityException;
import module.workflow.activities.ActivityInformation;
import module.workflow.activities.WorkflowActivity;
import module.workflow.domain.ProcessFile;
import module.workflow.domain.ProcessFileValidationException;
import module.workflow.domain.WorkflowProcess;
import module.workflow.domain.WorkflowProcessComment;
import module.workflow.presentationTier.WorkflowLayoutContext;
import module.workflow.servlet.WorkflowContainerInitializer;
import module.workflow.util.FileUploadBeanResolver;
import module.workflow.util.PresentableProcessState;
import module.workflow.util.WorkflowFileUploadBean;

import org.apache.commons.lang.StringUtils;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.fenixedu.bennu.core.domain.User;
import org.fenixedu.bennu.core.domain.exceptions.DomainException;
import org.fenixedu.bennu.core.security.Authenticate;
import org.fenixedu.bennu.portal.domain.MenuFunctionality;
import org.fenixedu.bennu.portal.model.Functionality;
import org.fenixedu.bennu.portal.servlet.BennuPortalDispatcher;
import org.fenixedu.bennu.struts.annotations.Mapping;
import org.fenixedu.bennu.struts.base.BaseAction;

import pt.ist.fenixWebFramework.renderers.utils.RenderUtils;
import pt.ist.fenixWebFramework.servlets.filters.contentRewrite.GenericChecksumRewriter;
import pt.ist.fenixframework.DomainObject;
import pt.ist.fenixframework.FenixFramework;

import com.google.gson.JsonObject;

/**
 * 
 * @author Diogo Figueiredo
 * @author Joo Neves
 * @author Joo Antunes
 * @author Pedro Santos
 * @author Shezad Anavarali
 * @author Paulo Abrantes
 * @author Luis Cruz
 * 
 */
@Mapping(path = "/workflowProcessManagement")
public class ProcessManagement extends BaseAction {

    public static final String workflowManagementURL = "/workflowProcessManagement.do?method=viewProcess&processId=";

    public static Map<Class<? extends WorkflowProcess>, ProcessRequestHandler<? extends WorkflowProcess>> handlers = new HashMap<Class<? extends WorkflowProcess>, ProcessRequestHandler<? extends WorkflowProcess>>();

    protected User getLoggedPerson() {
        return Authenticate.getUser();
    }

    private final ActionForward doForward(HttpServletRequest request, String body) {
        WorkflowProcess process = getProcess(request);
        WorkflowLayoutContext layout = process.getLayout();
        request.setAttribute("context", layout);
        if (BennuPortalDispatcher.getSelectedFunctionality(request) == null) {
            Functionality functionality = WorkflowContainerInitializer
                    .getFunctionalityForProcess(process.getClass());
            MenuFunctionality menuFunctionality = MenuFunctionality.findFunctionality(functionality.getProvider(),
                    functionality.getKey());
            BennuPortalDispatcher.selectFunctionality(request, menuFunctionality);
        }
        return forward(body);
    }

    public ActionForward viewProcess(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) throws ClassNotFoundException {

        WorkflowProcess process = getProcess(request);
        return viewProcess(process, request);
    }

    @SuppressWarnings("unchecked")
    public ActionForward viewProcess(WorkflowProcess process, final HttpServletRequest request) {

        request.setAttribute("process", process);
        ProcessRequestHandler<WorkflowProcess> handler = (ProcessRequestHandler<WorkflowProcess>) handlers
                .get(process.getClass());
        if (handler != null) {
            handler.handleRequest(process, request);
        }

        return doForward(request, "/workflow/viewProcess.jsp");
    }

    public ActionForward forwardToProcessPage(WorkflowProcess process, HttpServletRequest request) {

        ActionForward forward = new ActionForward();
        forward.setRedirect(true);
        String realPath = ProcessManagement.workflowManagementURL + process.getExternalId();
        forward.setPath(
                realPath + "&" + GenericChecksumRewriter.CHECKSUM_ATTRIBUTE_NAME + "=" + GenericChecksumRewriter
                        .calculateChecksum(request.getContextPath() + realPath, request.getSession()));
        return forward;
    }

    public ActionForward process(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        WorkflowProcess process = getProcess(request);
        WorkflowActivity<WorkflowProcess, ActivityInformation<WorkflowProcess>> activity = getActivity(process,
                request);
        ActivityInformation<WorkflowProcess> information = getRenderedObject("activityBean");
        return doLifeCycle(information, process, activity, request);
    }

    public ActionForward activityDefaultPostback(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        WorkflowProcess process = getProcess(request);
        WorkflowActivity<WorkflowProcess, ActivityInformation<WorkflowProcess>> activity = getActivity(process,
                request);
        ActivityInformation<WorkflowProcess> information = getRenderedObject("activityBean");
        RenderUtils.invalidateViewState();
        return executeActivity(process, request, activity, information);
    }

    public ActionForward actionLink(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) throws Exception {

        WorkflowProcess process = getProcess(request);
        WorkflowActivity<WorkflowProcess, ActivityInformation<WorkflowProcess>> activity = getActivity(process,
                request);
        ActivityInformation<WorkflowProcess> information = populateInformation(process, activity, request);
        return executeActivity(process, request, activity, information);
    }

    private ActivityInformation<WorkflowProcess> populateInformation(WorkflowProcess process,
            WorkflowActivity<WorkflowProcess, ActivityInformation<WorkflowProcess>> activity,
            HttpServletRequest request) throws Exception {
        ActivityInformation<WorkflowProcess> activityInformation = activity.getActivityInformation(process);
        String parameters = request.getParameter("parameters");
        Class<? extends ActivityInformation> activityClass = activityInformation.getClass();
        if (!StringUtils.isEmpty(parameters)) {
            for (String parameter : parameters.split(",")) {

                Field field = getField(activityClass, parameter);
                Class<?> type = field.getType();
                Object convertedValue = convert(type, request.getParameter(parameter));
                Method declaredMethod = getMethod(
                        "set" + parameter.substring(0, 1).toUpperCase() + parameter.substring(1), activityClass,
                        convertedValue.getClass());
                declaredMethod.invoke(activityInformation, convertedValue);
            }
        }
        return activityInformation;
    }

    private Field getField(Class activityClass, String parameter) throws SecurityException, NoSuchFieldException {
        if (activityClass == null) {
            throw new NoSuchFieldException();
        }
        Field field;
        try {
            field = activityClass.getDeclaredField(parameter);
        } catch (final NoSuchFieldException ex) {
            field = null;
        }
        return field == null ? getField(activityClass.getSuperclass(), parameter) : field;
    }

    private Method getMethod(String methodName, Class<? extends ActivityInformation> activityClass,
            Class<? extends Object> argumentClass) {
        Method method = null;
        while (!argumentClass.equals(Object.class)) {
            try {
                return activityClass.getMethod(methodName, argumentClass);
            } catch (NoSuchMethodException e) {
                argumentClass = argumentClass.getSuperclass();
            }
        }
        return method;
    }

    private Object convert(Class<?> type, String parameterValue) throws Exception {
        if (DomainObject.class.isAssignableFrom(type)) {
            return FenixFramework.getDomainObject(parameterValue);
        }
        if (type == Integer.class) {
            return Integer.parseInt(parameterValue);
        }
        if (type == Double.class) {
            return Double.parseDouble(parameterValue);
        }
        if (type == Float.class) {
            return Float.parseFloat(parameterValue);
        }
        if (type == String.class) {
            return parameterValue;
        }
        throw new IllegalArgumentException("Invalid type" + type.getName());
    }

    private ActionForward doLifeCycle(ActivityInformation<WorkflowProcess> information, WorkflowProcess process,
            WorkflowActivity<WorkflowProcess, ActivityInformation<WorkflowProcess>> activity,
            HttpServletRequest request) {
        if (information == null) {
            information = activity.getActivityInformation(process);
        } else {
            information.markHasForwardedFromInput();
        }

        return executeActivity(process, request, activity, information);
    }

    public ActionForward executeActivity(WorkflowProcess process, HttpServletRequest request,
            WorkflowActivity<WorkflowProcess, ActivityInformation<WorkflowProcess>> activity,
            ActivityInformation<WorkflowProcess> information) {
        if (information.hasAllneededInfo()) {

            try {
                activity.execute(information);
            } catch (ActivityException e) {
                addLocalizedMessage(request, e.getMessage());
                RenderUtils.invalidateViewState();
                return information.isForwardedFromInput() ? forwardProcessForInput(activity, request, information)
                        : viewProcess(process, request);
            } catch (DomainException domainEx) {
                addLocalizedMessage(request, domainEx.getLocalizedMessage());
                RenderUtils.invalidateViewState();
                return information.isForwardedFromInput() ? forwardProcessForInput(activity, request, information)
                        : viewProcess(process, request);
            } catch (ConsistencyException exc) {
                displayConsistencyException(exc, request);

                RenderUtils.invalidateViewState();
                return information.isForwardedFromInput() ? forwardProcessForInput(activity, request, information)
                        : viewProcess(process, request);
            }

            return forwardToProcessPage(process, request);
        }

        return forwardProcessForInput(activity, request, information);
    }

    private final <T extends WorkflowProcess> ActionForward forwardProcessForInput(WorkflowActivity activity,
            HttpServletRequest request, ActivityInformation<T> information) {
        request.setAttribute("information", information);
        if (activity.isDefaultInputInterfaceUsed()) {
            return doForward(request, "/workflow/activityInput.jsp");
        } else {
            request.setAttribute("inputInterface", activity.getClass().getName().replace('.', '/') + ".jsp");
            return doForward(request, "/workflow/nonDefaultActivityInput.jsp");
        }
    }

    public ActionForward viewComments(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        final WorkflowProcess process = getProcess(request);
        request.setAttribute("process", process);

        Set<WorkflowProcessComment> comments = new TreeSet<WorkflowProcessComment>(
                WorkflowProcessComment.COMPARATOR);
        comments.addAll(process.getCommentsSet());

        process.markCommentsAsReadForUser(Authenticate.getUser());
        request.setAttribute("comments", comments);
        request.setAttribute("bean", new CommentBean(process));

        return doForward(request, "/workflow/viewComments.jsp");
    }

    public ActionForward addComment(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        final WorkflowProcess process = getProcess(request);

        String displayedInlineString = request.getParameter("displayedInLine");
        Boolean displayedInline = Boolean.valueOf(displayedInlineString);

        CommentBean bean = getRenderedObject("comment");

        process.createComment(Authenticate.getUser(), bean);

        RenderUtils.invalidateViewState();
        if (!displayedInline) {
            return viewComments(mapping, form, request, response);
        } else {
            return viewProcess(process, request);
        }
    }

    private ActionForward forwardToUpload(HttpServletRequest request, WorkflowFileUploadBean bean) {

        if (!bean.isDefaultUploadInterfaceUsed()) {
            request.setAttribute("interface",
                    "/" + bean.getSelectedInstance().getName().replace('.', '/') + "-upload.jsp");
        }
        return doForward(request, "/workflow/fileUpload.jsp");
    }

    private ActionForward forwardToDocumentUpload(HttpServletRequest request, WorkflowFileUploadBean bean) {

        if (!bean.isDefaultUploadInterfaceUsed()) {
            request.setAttribute("interface",
                    "/" + bean.getSelectedInstance().getName().replace('.', '/') + "-upload.jsp");
        }
        return doForward(request, "/workflow/documentUpload.jsp");
    }

    public ActionForward documentFileUpload(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        final WorkflowProcess process = getProcess(request);
        Class<? extends ProcessFile> selectedInstance = process.getUploadableFileTypes().get(0);
        WorkflowFileUploadBean bean = FileUploadBeanResolver.getBeanForProcessFile(process, selectedInstance);
        bean.setSelectedInstance(selectedInstance);

        request.setAttribute("bean", bean);
        request.setAttribute("process", process);

        return forwardToDocumentUpload(request, bean);
    }

    public ActionForward fileUpload(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        final WorkflowProcess process = getProcess(request);
        Class<? extends ProcessFile> selectedInstance = process.getUploadableFileTypes().get(0);
        WorkflowFileUploadBean bean = FileUploadBeanResolver.getBeanForProcessFile(process, selectedInstance);
        bean.setSelectedInstance(selectedInstance);

        request.setAttribute("bean", bean);
        request.setAttribute("process", process);

        return forwardToUpload(request, bean);
    }

    public ActionForward upload(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) throws Exception {
        WorkflowFileUploadBean bean = getRenderedObject("uploadFile");
        final WorkflowProcess process = getProcess(request);

        try {
            process.addFile(bean.getSelectedInstance(), bean.getDisplayName(), bean.getFilename(),
                    consumeInputStream(bean.getInputStream()), bean);
        } catch (ProcessFileValidationException e) {
            request.setAttribute("bean", bean);
            request.setAttribute("process", process);
            addLocalizedMessage(request, e.getLocalizedMessage());
            return forwardToUpload(request, bean);
        } catch (DomainException e) {
            request.setAttribute("bean", bean);
            request.setAttribute("process", process);
            addLocalizedMessage(request, e.getLocalizedMessage());
            RenderUtils.invalidateViewState();
            return forwardToUpload(request, bean);
        }

        return viewProcess(process, request);

    }

    private WorkflowFileUploadBean fileUploadRoundTrip(Class<? extends ProcessFile> selectedInstance,
            final HttpServletRequest request) {

        final WorkflowProcess process = getProcess(request);
        WorkflowFileUploadBean bean = FileUploadBeanResolver.getBeanForProcessFile(process, selectedInstance);
        bean.setSelectedInstance(selectedInstance);

        request.setAttribute("bean", bean);
        request.setAttribute("process", process);

        return bean;
    }

    private WorkflowFileUploadBean documentUploadRoundTrip(Class<? extends ProcessFile> selectedInstance,
            final HttpServletRequest request) {

        final WorkflowProcess process = getProcess(request);
        WorkflowFileUploadBean bean = FileUploadBeanResolver.getBeanForProcessFile(process, selectedInstance);
        bean.setSelectedInstance(selectedInstance);

        request.setAttribute("bean", bean);
        request.setAttribute("process", process);

        return bean;
    }

    public ActionForward uploadPostBack(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {
        WorkflowFileUploadBean bean = getRenderedObject("uploadFile");
        bean = fileUploadRoundTrip(bean.getSelectedInstance(), request);

        RenderUtils.invalidateViewState("uploadFile");

        return forwardToUpload(request, bean);
    }

    public ActionForward invalidFileUpload(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {
        WorkflowFileUploadBean bean = getRenderedObject("uploadFile");
        bean = fileUploadRoundTrip(bean.getSelectedInstance(), request);

        return forwardToUpload(request, bean);
    }

    public ActionForward invalidDocumentUpload(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {
        WorkflowFileUploadBean bean = getRenderedObject("uploadFile");
        bean = documentUploadRoundTrip(bean.getSelectedInstance(), request);

        return forwardToDocumentUpload(request, bean);
    }

    public ActionForward viewLogs(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        final WorkflowProcess process = getProcess(request);

        request.setAttribute("operationLogs", process.getExecutionLogsSet());

        request.setAttribute("process", process);
        return doForward(request, "/workflow/viewLogs.jsp");
    }

    public ActionForward downloadFile(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) throws IOException {

        final ProcessFile file = getDomainObject(request, "fileId");
        WorkflowProcess process = file.getProcess();
        if (process != null) {
            process.preAccessFile(file);
        }
        download(response, file.getFilename(), file.getStream(), file.getContentType());
        if (process != null) {
            process.postAccessFile(file);
        }
        return null;
    }

    public ActionForward removeFile(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        final ProcessFile file = getDomainObject(request, "fileId");
        final WorkflowProcess process = file.getProcess();
        process.removeFiles(file);

        return viewProcess(process, request);

    }

    public ActionForward viewFilesDetails(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) {

        final WorkflowProcess process = getProcess(request);
        List<ProcessFile> listFiles = process.getFilesIncludingDeleted(process.getAvailableFileTypes(), true);
        request.setAttribute("process", process);
        request.setAttribute("listFiles", listFiles);
        return doForward(request, "/workflow/viewFilesDetails.jsp");

    }

    private <T extends WorkflowProcess> WorkflowActivity<T, ActivityInformation<T>> getActivity(
            WorkflowProcess process, HttpServletRequest request) {
        String activityName = request.getParameter("activity");
        return process.getActivity(activityName);
    }

    protected <T extends WorkflowProcess> T getProcess(HttpServletRequest request) {
        return (T) getDomainObject(request, "processId");
    }

    /**
     * Utility method that forwards to the main page of the process.
     * 
     * @param process
     *            to be seen
     * @return ActionFoward to the main page of the process
     */
    public static ActionForward forwardToProcess(final WorkflowProcess process) {
        return new ActionForward(
                "/workflowProcessManagement.do?method=viewProcess&processId=" + process.getExternalId());
    }

    /**
     * Utility method that forwards to the activity page (or executes it in case
     * it doesn't need any input) for a given process
     * 
     * @param process
     *            the process to run the activity against
     * @param activity
     *            the activity to run
     * @return ActionForward to the Activity running lifecycle entry point
     */
    public static ActionForward forwardToActivity(final WorkflowProcess process, final WorkflowActivity activity) {
        return new ActionForward("/workflowProcessManagement.do?method=process&processId=" + process.getExternalId()
                + "&activity=" + activity.getClass().getSimpleName());
    }

    /**
     * Even though a default postback method is given -
     * {@link ProcessManagement#activityDefaultPostback(ActionMapping, ActionForm, HttpServletRequest, HttpServletResponse)} - in
     * case some other operations should be done during the postback, this
     * method should be use.
     * 
     * Define the postback to your own postback destination and then in that
     * method return the result of this method.
     * 
     * @param information
     *            from the information postback
     * @param request
     *            the httpservlet request
     * @return ActionForward for the activity input interface.
     */
    public ActionForward performActivityPostback(ActivityInformation<? extends WorkflowProcess> information,
            HttpServletRequest request) {
        return forwardProcessForInput(information.getActivity(), request, information);
    }

    /*
     * Use this method to register objects of the type {@link ProcessRequestHandler}
     * 
     * @param workflowProcessClass
     *            The type of class for which the handle will perform work
     * @param handler
     *            the handler that performs the specific code
     */
    public static <T extends WorkflowProcess> void registerProcessRequestHandler(Class<T> workflowProcessClass,
            ProcessRequestHandler<T> handler) {
        handlers.put(workflowProcessClass, handler);
    }

    /**
     * The ProcessRequestHandler is an handler that allows the user to take
     * control of the HttpServletRequest before the main page of the process is
     * viewed. This is useful if there are layout changes and more objects want
     * to be put in the request.
     * 
     * @param <T>: Some kind of WorkflowProcess
     */
    public static interface ProcessRequestHandler<T extends WorkflowProcess> {
        public void handleRequest(T process, HttpServletRequest request);
    }

    public ActionForward viewTypeDescription(final ActionMapping mapping, final ActionForm form,
            final HttpServletRequest request, final HttpServletResponse response) throws IOException {
        String classname = request.getParameter("classname");
        int indexOfInnerClassInEnum = classname.indexOf("$");
        if (indexOfInnerClassInEnum > 0) {
            classname = classname.substring(0, indexOfInnerClassInEnum);
        }
        PresentableProcessState type;
        try {
            Class<Enum> stateEnum = (Class<Enum>) Class.forName(classname);
            type = (PresentableProcessState) Enum.valueOf(stateEnum, request.getParameter("type"));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        request.setAttribute("name", type.getLocalizedName());

        JsonObject reply = new JsonObject();

        reply.addProperty("name", type.getLocalizedName());
        reply.addProperty("description", type.getDescription());

        try (OutputStream outputStream = response.getOutputStream()) {
            byte[] jsonReply = reply.toString().getBytes();
            response.setContentType("text");
            response.setContentLength(jsonReply.length);
            outputStream.write(jsonReply);
            outputStream.flush();
        }

        return null;
    }

}