com.evolveum.midpoint.web.page.admin.server.PageTaskEdit.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.web.page.admin.server.PageTaskEdit.java

Source

/*
 * Copyright (c) 2010-2013 Evolveum
 *
 * 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 com.evolveum.midpoint.web.page.admin.server;

import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskBinding;
import com.evolveum.midpoint.task.api.TaskManager;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.application.AuthorizationAction;
import com.evolveum.midpoint.web.application.PageDescriptor;
import com.evolveum.midpoint.web.component.AjaxButton;
import com.evolveum.midpoint.web.component.AjaxSubmitButton;
import com.evolveum.midpoint.web.component.DateInput;
import com.evolveum.midpoint.web.component.data.TablePanel;
import com.evolveum.midpoint.web.component.data.column.LinkPanel;
import com.evolveum.midpoint.web.component.model.operationStatus.ModelOperationStatusDto;
import com.evolveum.midpoint.web.component.model.operationStatus.ModelOperationStatusPanel;
import com.evolveum.midpoint.web.component.util.ListDataProvider;
import com.evolveum.midpoint.web.component.util.LoadableModel;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.page.PageBase;
import com.evolveum.midpoint.web.page.admin.server.dto.ScheduleValidator;
import com.evolveum.midpoint.web.page.admin.server.dto.StartEndDateValidator;
import com.evolveum.midpoint.web.page.admin.server.dto.TaskDto;
import com.evolveum.midpoint.web.page.admin.server.dto.TaskDtoExecutionStatus;
import com.evolveum.midpoint.web.page.admin.server.dto.TaskDtoProviderOptions;
import com.evolveum.midpoint.web.page.admin.server.subtasks.SubtasksPanel;
import com.evolveum.midpoint.web.page.admin.server.workflowInformation.WorkflowInformationPanel;
import com.evolveum.midpoint.web.page.admin.users.PageAdminUsers;
import com.evolveum.midpoint.web.resource.img.ImgResources;
import com.evolveum.midpoint.web.util.InfoTooltipBehavior;
import com.evolveum.midpoint.web.util.OnePageParameterEncoder;
import com.evolveum.midpoint.web.util.WebMiscUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MisfireActionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ScheduleType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ThreadStopActionType;

import org.apache.commons.lang.StringUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.util.string.StringValue;

import java.util.*;

/**
 * @author lazyman
 * @author mserbak
 */
@PageDescriptor(url = "/admin/task", encoder = OnePageParameterEncoder.class, action = {
        @AuthorizationAction(actionUri = PageAdminTasks.AUTHORIZATION_TASKS_ALL, label = PageAdminTasks.AUTH_TASKS_ALL_LABEL, description = PageAdminTasks.AUTH_TASKS_ALL_DESCRIPTION),
        @AuthorizationAction(actionUri = AuthorizationConstants.NS_AUTHORIZATION
                + "#task", label = "PageTaskEdit.auth.task.label", description = "PageTaskEdit.auth.task.description") })
public class PageTaskEdit extends PageAdminTasks {

    private static final Trace LOGGER = TraceManager.getTrace(PageTaskEdit.class);
    private static final String DOT_CLASS = PageTaskAdd.class.getName() + ".";
    private static final String OPERATION_LOAD_TASK = DOT_CLASS + "loadTask";
    private static final String OPERATION_SAVE_TASK = DOT_CLASS + "saveTask";
    private static final String OPERATION_SUSPEND_TASKS = DOT_CLASS + "suspendTask";
    private static final String OPERATION_RESUME_TASK = DOT_CLASS + "resumeTask";
    private static final String OPERATION_RUN_NOW_TASK = DOT_CLASS + "runNowTask";

    private static final String ID_MAIN_FORM = "mainForm";
    private static final String ID_IDENTIFIER = "identifier";
    private static final String ID_HANDLER_URI_LIST = "handlerUriList";
    private static final String ID_HANDLER_URI = "handlerUri";
    private static final String ID_MODEL_OPERATION_STATUS_LABEL = "modelOperationStatusLabel";
    private static final String ID_MODEL_OPERATION_STATUS_PANEL = "modelOperationStatusPanel";
    private static final String ID_SUBTASKS_LABEL = "subtasksLabel";
    private static final String ID_SUBTASKS_PANEL = "subtasksPanel";
    private static final String ID_WORKFLOW_INFORMATION_LABEL = "workflowInformationLabel";
    private static final String ID_WORKFLOW_INFORMATION_PANEL = "workflowInformationPanel";
    private static final String ID_NAME = "name";
    private static final String ID_NAME_LABEL = "nameLabel";
    private static final String ID_DESCRIPTION = "description";
    private static final String ID_DESCRIPTION_LABEL = "descriptionLabel";
    private static final String ID_PARENT = "parent";
    private static final String ID_OPERATION_RESULT_PANEL = "operationResultPanel";
    private static final String ID_SUSPEND = "suspend";
    private static final String ID_RESUME = "resume";
    private static final String ID_RUN_NOW = "runNow";
    private static final String ID_DRY_RUN = "dryRun";

    private IModel<TaskDto> model;
    private static boolean edit = false;

    private PageParameters parameters;

    public PageTaskEdit() {
        this(new PageParameters(), null);
    }

    public PageTaskEdit(PageParameters parameters, PageBase previousPage) {

        this.parameters = parameters;
        setPreviousPage(previousPage);

        model = new LoadableModel<TaskDto>(false) {

            @Override
            protected TaskDto load() {
                return loadTask();
            }
        };

        edit = false;
        initLayout();
    }

    private boolean isRunnableOrRunning() {
        TaskDtoExecutionStatus exec = model.getObject().getExecution();
        return TaskDtoExecutionStatus.RUNNABLE.equals(exec) || TaskDtoExecutionStatus.RUNNING.equals(exec);
    }

    private boolean isRunnable() {
        TaskDtoExecutionStatus exec = model.getObject().getExecution();
        return TaskDtoExecutionStatus.RUNNABLE.equals(exec);
    }

    private boolean isRunning() {
        TaskDtoExecutionStatus exec = model.getObject().getExecution();
        return TaskDtoExecutionStatus.RUNNING.equals(exec);
    }

    private boolean isClosed() {
        TaskDtoExecutionStatus exec = model.getObject().getExecution();
        return TaskDtoExecutionStatus.CLOSED.equals(exec);
    }

    private boolean isRecurring() {
        return model.getObject().getRecurring();
    }

    private boolean isSuspended() {
        TaskDtoExecutionStatus exec = model.getObject().getExecution();
        return TaskDtoExecutionStatus.SUSPENDED.equals(exec);
    }

    private TaskDto loadTask() {
        OperationResult result = new OperationResult(OPERATION_LOAD_TASK);
        Task operationTask = getTaskManager().createTaskInstance(OPERATION_LOAD_TASK);

        StringValue taskOid = parameters.get(OnePageParameterEncoder.PARAMETER);

        TaskDto taskDto = null;
        try {
            Collection<SelectorOptions<GetOperationOptions>> options = GetOperationOptions
                    .createRetrieveAttributesOptions(TaskType.F_SUBTASK, TaskType.F_NODE_AS_OBSERVED,
                            TaskType.F_NEXT_RUN_START_TIMESTAMP);
            TaskType loadedTask = getModelService()
                    .getObject(TaskType.class, taskOid.toString(), options, operationTask, result).asObjectable();
            taskDto = prepareTaskDto(loadedTask, result);
            result.computeStatus();
        } catch (Exception ex) {
            result.recordFatalError("Couldn't get task.", ex);
        }

        if (!result.isSuccess()) {
            showResult(result);
        }

        if (taskDto == null) {
            getSession().error(getString("pageTaskEdit.message.cantTaskDetails"));
            if (!result.isSuccess()) {
                showResultInSession(result);
            }
            throw getRestartResponseException(PageTasks.class);
        }
        return taskDto;
    }

    private TaskDto prepareTaskDto(TaskType task, OperationResult result)
            throws SchemaException, ObjectNotFoundException {
        TaskDto taskDto = new TaskDto(task, getModelService(), getTaskService(), getModelInteractionService(),
                getTaskManager(), TaskDtoProviderOptions.fullOptions(), result);
        for (TaskType child : task.getSubtask()) {
            taskDto.addChildTaskDto(prepareTaskDto(child, result));
        }
        return taskDto;
    }

    private void initLayout() {
        Form mainForm = new Form(ID_MAIN_FORM);
        add(mainForm);

        initMainInfo(mainForm);
        initSchedule(mainForm);

        VisibleEnableBehaviour hiddenWhenEditing = new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return !edit;
            }
        };

        VisibleEnableBehaviour hiddenWhenEditingOrNoSubtasks = new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return !edit && !model.getObject().getSubtasks().isEmpty();
            }
        };

        Label subtasksLabel = new Label(ID_SUBTASKS_LABEL, new ResourceModel("pageTaskEdit.subtasksLabel"));
        subtasksLabel.add(hiddenWhenEditingOrNoSubtasks);
        mainForm.add(subtasksLabel);
        SubtasksPanel subtasksPanel = new SubtasksPanel(ID_SUBTASKS_PANEL,
                new PropertyModel<List<TaskDto>>(model, TaskDto.F_SUBTASKS), getWorkflowManager().isEnabled());
        subtasksPanel.add(hiddenWhenEditingOrNoSubtasks);
        mainForm.add(subtasksPanel);

        VisibleEnableBehaviour hiddenWhenEditingOrNoWorkflowInformation = new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return !edit && model.getObject().isWorkflowShadowTask() && getWorkflowManager().isEnabled();
            }
        };

        Label workflowInformationLabel = new Label(ID_WORKFLOW_INFORMATION_LABEL,
                new ResourceModel("pageTaskEdit.workflowInformationLabel"));
        workflowInformationLabel.add(hiddenWhenEditingOrNoWorkflowInformation);
        mainForm.add(workflowInformationLabel);

        WorkflowInformationPanel workflowInformationPanel = new WorkflowInformationPanel(
                ID_WORKFLOW_INFORMATION_PANEL, model);
        workflowInformationPanel.add(hiddenWhenEditingOrNoWorkflowInformation);
        mainForm.add(workflowInformationPanel);

        VisibleEnableBehaviour modelOpBehaviour = new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return !edit && model.getObject().getModelOperationStatus() != null;
            }
        };
        Label modelOperationStatusLabel = new Label(ID_MODEL_OPERATION_STATUS_LABEL,
                new ResourceModel("pageTaskEdit.modelOperationStatusLabel"));
        modelOperationStatusLabel.add(modelOpBehaviour);
        mainForm.add(modelOperationStatusLabel);
        ModelOperationStatusPanel panel = new ModelOperationStatusPanel(ID_MODEL_OPERATION_STATUS_PANEL,
                new PropertyModel<ModelOperationStatusDto>(model, TaskDto.F_MODEL_OPERATION_STATUS));
        panel.add(modelOpBehaviour);
        mainForm.add(panel);

        SortableDataProvider<OperationResult, String> provider = new ListDataProvider<OperationResult>(this,
                new PropertyModel<List<OperationResult>>(model, "opResult"));
        TablePanel result = new TablePanel<OperationResult>("operationResult", provider, initResultColumns());
        result.setStyle("padding-top: 0px;");
        result.setShowPaging(false);
        result.setOutputMarkupId(true);
        result.add(hiddenWhenEditing);
        mainForm.add(result);

        DropDownChoice threadStop = new DropDownChoice("threadStop", new Model<ThreadStopActionType>() {

            @Override
            public ThreadStopActionType getObject() {
                return model.getObject().getThreadStop();
            }

            @Override
            public void setObject(ThreadStopActionType object) {
                model.getObject().setThreadStop(object);
            }
        }, WebMiscUtil.createReadonlyModelFromEnum(ThreadStopActionType.class),
                new EnumChoiceRenderer<ThreadStopActionType>(PageTaskEdit.this));
        threadStop.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isEnabled() {
                return edit;
            }
        });
        mainForm.add(threadStop);

        //mainForm.add(new TsaValidator(runUntilNodeDown, threadStop));

        CheckBox dryRun = new CheckBox(ID_DRY_RUN, new PropertyModel<Boolean>(model, TaskDto.F_DRY_RUN));
        dryRun.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isEnabled() {
                return edit;
            }
        });
        mainForm.add(dryRun);

        initButtons(mainForm);
    }

    private void initMainInfo(Form mainForm) {
        RequiredTextField<String> name = new RequiredTextField<String>(ID_NAME,
                new PropertyModel<String>(model, TaskDto.F_NAME));
        name.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                return edit;
            }
        });
        name.add(new AttributeModifier("style", "width: 100%"));
        name.add(new EmptyOnBlurAjaxFormUpdatingBehaviour());
        mainForm.add(name);

        Label nameLabel = new Label(ID_NAME_LABEL, new PropertyModel(model, TaskDto.F_NAME));
        nameLabel.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return !edit;
            }
        });
        mainForm.add(nameLabel);

        TextArea<String> description = new TextArea<String>(ID_DESCRIPTION,
                new PropertyModel<String>(model, TaskDto.F_DESCRIPTION));
        description.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                return edit;
            }
        });
        //        description.add(new AttributeModifier("style", "width: 100%"));
        //        description.add(new EmptyOnBlurAjaxFormUpdatingBehaviour());
        mainForm.add(description);

        Label descriptionLabel = new Label(ID_DESCRIPTION_LABEL, new PropertyModel(model, TaskDto.F_DESCRIPTION));
        descriptionLabel.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return !edit;
            }
        });
        mainForm.add(descriptionLabel);

        Label oid = new Label("oid", new PropertyModel(model, "oid"));
        mainForm.add(oid);

        mainForm.add(new Label(ID_IDENTIFIER, new PropertyModel(model, TaskDto.F_IDENTIFIER)));

        Label category = new Label("category", new PropertyModel(model, "category"));
        mainForm.add(category);

        LinkPanel parent = new LinkPanel(ID_PARENT, new PropertyModel(model, TaskDto.F_PARENT_TASK_NAME)) {
            @Override
            public void onClick(AjaxRequestTarget target) {
                String oid = model.getObject().getParentTaskOid();
                if (oid != null) {
                    PageParameters parameters = new PageParameters();
                    parameters.add(OnePageParameterEncoder.PARAMETER, oid);
                    setResponsePage(new PageTaskEdit(parameters, PageTaskEdit.this));
                }
            }
        };
        mainForm.add(parent);

        ListView<String> handlerUriList = new ListView<String>(ID_HANDLER_URI_LIST,
                new PropertyModel(model, TaskDto.F_HANDLER_URI_LIST)) {
            @Override
            protected void populateItem(ListItem<String> item) {
                item.add(new Label(ID_HANDLER_URI, item.getModelObject()));
            }
        };
        mainForm.add(handlerUriList);
        //      Label uri = new Label(ID_HANDLER_URI, new PropertyModel(model, "uri"));
        //      mainForm.add(uri);

        Label execution = new Label("execution", new AbstractReadOnlyModel<String>() {

            @Override
            public String getObject() {
                TaskDtoExecutionStatus executionStatus = model.getObject().getExecution();
                if (executionStatus != TaskDtoExecutionStatus.CLOSED) {
                    return getString(TaskDtoExecutionStatus.class.getSimpleName() + "." + executionStatus.name());
                } else {
                    return getString(TaskDtoExecutionStatus.class.getSimpleName() + "." + executionStatus.name()
                            + ".withTimestamp", new AbstractReadOnlyModel<String>() {
                                @Override
                                public String getObject() {
                                    if (model.getObject().getCompletionTimestamp() != null) {
                                        return new Date(model.getObject().getCompletionTimestamp())
                                                .toLocaleString(); // todo correct formatting
                                    } else {
                                        return "?";
                                    }
                                }
                            });
                }
            }
        });
        mainForm.add(execution);

        Label node = new Label("node", new AbstractReadOnlyModel<String>() {
            @Override
            public String getObject() {
                TaskDto dto = model.getObject();
                if (!TaskDtoExecutionStatus.RUNNING.equals(dto.getExecution())) {
                    return null;
                }
                return PageTaskEdit.this.getString("pageTaskEdit.message.node", dto.getExecutingAt());
            }
        });
        mainForm.add(node);
    }

    private void initSchedule(Form mainForm) {
        //todo probably can be removed, visibility can be updated in children (already components) [lazyman]
        final WebMarkupContainer container = new WebMarkupContainer("container");
        container.setOutputMarkupId(true);
        mainForm.add(container);

        final IModel<Boolean> recurringCheck = new PropertyModel<Boolean>(model, "recurring");
        final IModel<Boolean> boundCheck = new PropertyModel<Boolean>(model, "bound");

        WebMarkupContainer suspendReqRecurring = new WebMarkupContainer("suspendReqRecurring");
        suspendReqRecurring.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return edit && isRunnableOrRunning();
            }
        });
        mainForm.add(suspendReqRecurring);

        final WebMarkupContainer boundContainer = new WebMarkupContainer("boundContainer");
        boundContainer.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                return recurringCheck.getObject();
            }

        });
        boundContainer.setOutputMarkupId(true);
        container.add(boundContainer);

        WebMarkupContainer suspendReqBound = new WebMarkupContainer("suspendReqBound");
        suspendReqBound.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return edit && isRunnableOrRunning();
            }
        });
        boundContainer.add(suspendReqBound);

        final WebMarkupContainer intervalContainer = new WebMarkupContainer("intervalContainer");
        intervalContainer.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                return recurringCheck.getObject();
            }

        });
        intervalContainer.setOutputMarkupId(true);
        container.add(intervalContainer);

        final WebMarkupContainer cronContainer = new WebMarkupContainer("cronContainer");
        cronContainer.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                return recurringCheck.getObject() && !boundCheck.getObject();
            }

        });
        cronContainer.setOutputMarkupId(true);
        container.add(cronContainer);
        AjaxCheckBox recurring = new AjaxCheckBox("recurring", recurringCheck) {

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                target.add(container);
                target.add(PageTaskEdit.this.get("mainForm:recurring"));
            }
        };
        recurring.setOutputMarkupId(true);
        recurring.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isEnabled() {
                return edit && !isRunnableOrRunning();
            }
        });
        mainForm.add(recurring);

        final AjaxCheckBox bound = new AjaxCheckBox("bound", boundCheck) {

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                target.add(container);
                target.add(PageTaskEdit.this.get("mainForm:recurring"));
            }
        };
        bound.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isEnabled() {
                return edit && !isRunnableOrRunning();
            }
        });
        boundContainer.add(bound);

        Label boundHelp = new Label("boundHelp");
        boundHelp.add(new InfoTooltipBehavior());
        boundContainer.add(boundHelp);

        TextField<Integer> interval = new TextField<Integer>("interval",
                new PropertyModel<Integer>(model, "interval"));
        interval.add(new EmptyOnBlurAjaxFormUpdatingBehaviour());
        interval.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isEnabled() {
                return edit && (!isRunnableOrRunning() || !boundCheck.getObject());
            }
        });
        intervalContainer.add(interval);

        TextField<String> cron = new TextField<String>("cron",
                new PropertyModel<String>(model, "cronSpecification"));
        cron.add(new EmptyOnBlurAjaxFormUpdatingBehaviour());
        cron.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isEnabled() {
                return edit && (!isRunnableOrRunning() || !boundCheck.getObject());
            }
        });
        cronContainer.add(cron);

        Label cronHelp = new Label("cronHelp");
        cronHelp.add(new InfoTooltipBehavior());
        cronContainer.add(cronHelp);

        DateInput notStartBefore = new DateInput("notStartBeforeField",
                new PropertyModel<Date>(model, "notStartBefore"));
        notStartBefore.setOutputMarkupId(true);
        notStartBefore.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isEnabled() {
                return edit && !isRunning();
            }
        });
        mainForm.add(notStartBefore);

        DateInput notStartAfter = new DateInput("notStartAfterField",
                new PropertyModel<Date>(model, "notStartAfter"));
        notStartAfter.setOutputMarkupId(true);
        notStartAfter.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isEnabled() {
                return edit;
            }
        });
        mainForm.add(notStartAfter);

        DropDownChoice misfire = new DropDownChoice("misfireAction",
                new PropertyModel<MisfireActionType>(model, "misfireAction"),
                WebMiscUtil.createReadonlyModelFromEnum(MisfireActionType.class),
                new EnumChoiceRenderer<MisfireActionType>(PageTaskEdit.this));
        misfire.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isEnabled() {
                return edit;
            }
        });
        mainForm.add(misfire);

        Label lastStart = new Label("lastStarted", new AbstractReadOnlyModel<String>() {

            @Override
            public String getObject() {
                TaskDto dto = model.getObject();
                if (dto.getLastRunStartTimestampLong() == null) {
                    return "-";
                }
                Date date = new Date(dto.getLastRunStartTimestampLong());
                return WebMiscUtil.formatDate(date);
            }

        });
        mainForm.add(lastStart);

        Label lastFinished = new Label("lastFinished", new AbstractReadOnlyModel<String>() {

            @Override
            public String getObject() {
                TaskDto dto = model.getObject();
                if (dto.getLastRunFinishTimestampLong() == null) {
                    return "-";
                }
                Date date = new Date(dto.getLastRunFinishTimestampLong());
                return WebMiscUtil.formatDate(date);
            }
        });
        mainForm.add(lastFinished);

        Label nextRun = new Label("nextRun", new AbstractReadOnlyModel<String>() {

            @Override
            public String getObject() {
                TaskDto dto = model.getObject();
                if (dto.getRecurring() && dto.getBound() && isRunning()) {
                    return getString("pageTasks.runsContinually");
                }
                if (dto.getNextRunStartTimeLong() == null) {
                    return "-";
                }
                Date date = new Date(dto.getNextRunStartTimeLong());
                return WebMiscUtil.formatDate(date);
            }
        });
        mainForm.add(nextRun);

        mainForm.add(new StartEndDateValidator(notStartBefore, notStartAfter));
        mainForm.add(new ScheduleValidator(getTaskManager(), recurring, bound, interval, cron));
    }

    private void initButtons(final Form mainForm) {
        AjaxButton backButton = new AjaxButton("backButton", createStringResource("pageTaskEdit.button.back")) {

            @Override
            public void onClick(AjaxRequestTarget target) {
                edit = false;
                goBack(PageTasks.class);
            }
        };
        mainForm.add(backButton);

        AjaxSubmitButton saveButton = new AjaxSubmitButton("saveButton",
                createStringResource("pageTaskEdit.button.save")) {

            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                savePerformed(target);
            }

            @Override
            protected void onError(AjaxRequestTarget target, Form<?> form) {
                target.add(getFeedbackPanel());
            }

        };
        saveButton.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return edit;
            }
        });
        mainForm.add(saveButton);

        AjaxButton editButton = new AjaxButton("editButton", createStringResource("pageTaskEdit.button.edit")) {

            @Override
            public void onClick(AjaxRequestTarget target) {
                edit = true;
                target.add(mainForm);
            }
        };
        editButton.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return !edit;
            }
        });
        mainForm.add(editButton);

        AjaxButton suspend = new AjaxButton(ID_SUSPEND, createStringResource("pageTaskEdit.button.suspend")) {

            @Override
            public void onClick(AjaxRequestTarget target) {
                suspendPerformed(target);
            }
        };
        suspend.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                return !edit && isRunnableOrRunning();
            }
        });
        mainForm.add(suspend);

        AjaxButton resume = new AjaxButton(ID_RESUME, createStringResource("pageTaskEdit.button.resume")) {

            @Override
            public void onClick(AjaxRequestTarget target) {
                resumePerformed(target);
            }
        };
        resume.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                return !edit && (isSuspended() || (isClosed() && isRecurring()));
            }
        });
        mainForm.add(resume);

        AjaxButton runNow = new AjaxButton(ID_RUN_NOW, createStringResource("pageTaskEdit.button.runNow")) {

            @Override
            public void onClick(AjaxRequestTarget target) {
                runNowPerformed(target);
            }
        };
        runNow.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                return !edit && (isRunnable() || (isClosed() && !isRecurring()));
            }
        });
        mainForm.add(runNow);
    }

    private List<IColumn<OperationResult, String>> initResultColumns() {
        List<IColumn<OperationResult, String>> columns = new ArrayList<IColumn<OperationResult, String>>();

        columns.add(new PropertyColumn(createStringResource("pageTaskEdit.opResult.token"), "token"));
        columns.add(new PropertyColumn(createStringResource("pageTaskEdit.opResult.operation"), "operation"));
        columns.add(new PropertyColumn(createStringResource("pageTaskEdit.opResult.status"), "status"));
        columns.add(new PropertyColumn(createStringResource("pageTaskEdit.opResult.message"), "message"));
        return columns;
    }

    private void savePerformed(AjaxRequestTarget target) {
        LOGGER.debug("Saving new task.");
        OperationResult result = new OperationResult(OPERATION_SAVE_TASK);
        TaskDto dto = model.getObject();
        Task operationTask = createSimpleTask(OPERATION_SAVE_TASK);
        TaskManager manager = getTaskManager();

        try {
            PrismObject<TaskType> originalTaskType = getModelService().getObject(TaskType.class, dto.getOid(), null,
                    operationTask, result);
            Task originalTask = manager.createTaskInstance(originalTaskType, result);
            Task updatedTask = updateTask(dto, originalTask);

            if (LOGGER.isTraceEnabled()) {
                LOGGER.trace("Saving task modifications.");
            }
            getModelService().executeChanges(prepareChanges(updatedTask), null, operationTask, result);

            edit = false;
            setResponsePage(PageTasks.class);
            result.recomputeStatus();
        } catch (Exception ex) {
            result.recomputeStatus();
            result.recordFatalError("Couldn't save task.", ex);
            LoggingUtils.logException(LOGGER, "Couldn't save task modifications", ex);
        }
        showResultInSession(result);
        target.add(getFeedbackPanel());
    }

    private List<ObjectDelta<? extends ObjectType>> prepareChanges(Task updatedTask) {
        Collection<ItemDelta<?>> modifications = updatedTask.getPendingModifications();
        List<ObjectDelta<? extends ObjectType>> retval = new ArrayList<ObjectDelta<? extends ObjectType>>();
        retval.add(ObjectDelta.createModifyDelta(updatedTask.getOid(), modifications, TaskType.class,
                getPrismContext()));
        return retval;
    }

    private Task updateTask(TaskDto dto, Task existingTask) throws SchemaException {

        if (!existingTask.getName().equals(dto.getName())) {
            existingTask.setName(WebMiscUtil.createPolyFromOrigString(dto.getName()));
        } // if they are equal, modifyObject complains ... it's probably a bug in repo; we'll fix it later?

        if ((existingTask.getDescription() == null && dto.getDescription() != null)
                || (existingTask.getDescription() != null
                        && !existingTask.getDescription().equals(dto.getDescription()))) {
            existingTask.setDescription(dto.getDescription());
        }

        if (!dto.getRecurring()) {
            existingTask.makeSingle();
        }
        existingTask.setBinding(dto.getBound() == true ? TaskBinding.TIGHT : TaskBinding.LOOSE);

        ScheduleType schedule = new ScheduleType();

        schedule.setEarliestStartTime(MiscUtil.asXMLGregorianCalendar(dto.getNotStartBefore()));
        schedule.setLatestStartTime(MiscUtil.asXMLGregorianCalendar(dto.getNotStartAfter()));
        schedule.setMisfireAction(dto.getMisfire());
        if (existingTask.getSchedule() != null) {
            schedule.setLatestFinishTime(existingTask.getSchedule().getLatestFinishTime());
        }

        if (dto.getRecurring() == true) {

            if (dto.getBound() == false && dto.getCronSpecification() != null) {
                schedule.setCronLikePattern(dto.getCronSpecification());
            } else {
                schedule.setInterval(dto.getInterval());
            }
            existingTask.makeRecurring(schedule);
        } else {
            existingTask.makeSingle(schedule);
        }

        ThreadStopActionType tsa = dto.getThreadStop();
        //        if (tsa == null) {
        //            tsa = dto.getRunUntilNodeDown() ? ThreadStopActionType.CLOSE : ThreadStopActionType.RESTART;
        //        }
        existingTask.setThreadStopAction(tsa);

        if (dto.isDryRun()) {
            SchemaRegistry registry = getPrismContext().getSchemaRegistry();
            PrismPropertyDefinition def = registry
                    .findPropertyDefinitionByElementName(SchemaConstants.MODEL_EXTENSION_DRY_RUN);
            PrismProperty dryRun = new PrismProperty(SchemaConstants.MODEL_EXTENSION_DRY_RUN);
            dryRun.setDefinition(def);
            dryRun.setRealValue(true);

            existingTask.addExtensionProperty(dryRun);
        } else {
            PrismProperty dryRun = existingTask.getExtensionProperty(SchemaConstants.MODEL_EXTENSION_DRY_RUN);
            if (dryRun != null) {
                existingTask.deleteExtensionProperty(dryRun);
            }
        }
        return existingTask;
    }

    private void suspendPerformed(AjaxRequestTarget target) {
        String oid = model.getObject().getOid();
        OperationResult result = new OperationResult(OPERATION_SUSPEND_TASKS);
        try {
            boolean suspended = getTaskService().suspendTasks(Collections.singleton(oid),
                    PageTasks.WAIT_FOR_TASK_STOP, result);

            result.computeStatus();
            if (result.isSuccess()) {
                if (suspended) {
                    result.recordStatus(OperationResultStatus.SUCCESS,
                            "The task have been successfully suspended.");
                } else {
                    result.recordWarning(
                            "Task suspension has been successfully requested; please check for its completion using task list.");
                }
            }
        } catch (RuntimeException e) {
            result.recordFatalError("Couldn't suspend the task due to an unexpected exception", e);
        }

        showResultInSession(result);
        setResponsePage(PageTasks.class);
    }

    private void resumePerformed(AjaxRequestTarget target) {
        String oid = model.getObject().getOid();
        OperationResult result = new OperationResult(OPERATION_RESUME_TASK);
        try {
            getTaskService().resumeTasks(Arrays.asList(oid), result);
            result.computeStatus();

            if (result.isSuccess()) {
                result.recordStatus(OperationResultStatus.SUCCESS, "The task has been successfully resumed.");
            }
        } catch (RuntimeException e) {
            result.recordFatalError("Couldn't resume the task due to an unexpected exception", e);
        }

        showResultInSession(result);
        setResponsePage(PageTasks.class);
    }

    private void runNowPerformed(AjaxRequestTarget target) {
        String oid = model.getObject().getOid();
        OperationResult result = new OperationResult(OPERATION_RUN_NOW_TASK);
        try {
            getTaskService().scheduleTasksNow(Arrays.asList(oid), result);
            result.computeStatus();

            if (result.isSuccess()) {
                result.recordStatus(OperationResultStatus.SUCCESS,
                        "The task has been successfully scheduled to run.");
            }
        } catch (RuntimeException e) {
            result.recordFatalError("Couldn't schedule the task due to an unexpected exception", e);
        }

        showResultInSession(result);
        setResponsePage(PageTasks.class);
    }

    private static class EmptyOnBlurAjaxFormUpdatingBehaviour extends AjaxFormComponentUpdatingBehavior {

        public EmptyOnBlurAjaxFormUpdatingBehaviour() {
            super("onBlur");
        }

        @Override
        protected void onUpdate(AjaxRequestTarget target) {
        }
    }

    @Override
    public PageBase reinitialize() {
        return new PageTaskEdit(parameters, getPreviousPage());
    }
}