/*
* Copyright (c) 2010-2016 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.gui.api.model.LoadableModel;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.DateInput;
import com.evolveum.midpoint.web.component.form.Form;
import com.evolveum.midpoint.web.component.objectdetails.AbstractObjectTabPanel;
import com.evolveum.midpoint.web.component.prism.ObjectWrapper;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnBlurAjaxFormUpdatingBehaviour;
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.util.InfoTooltipBehavior;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MisfireActionType;
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.time.DurationFormatUtils;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.EnumChoiceRenderer;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.form.validation.IFormValidator;
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 java.util.Collection;
import java.util.Collections;
import java.util.Date;
/**
* @author semancik
* @author lazyman
* @author mserbak
* @author mederly
*/
public class TaskSchedulingTabPanel extends AbstractObjectTabPanel<TaskType> implements TaskTabPanel {
private static final long serialVersionUID = 1L;
private static final Trace LOGGER = TraceManager.getTrace(TaskSchedulingTabPanel.class);
public static final String ID_LAST_STARTED_CONTAINER = "lastStartedContainer";
public static final String ID_LAST_STARTED = "lastStarted";
public static final String ID_LAST_STARTED_AGO = "lastStartedAgo";
public static final String ID_LAST_FINISHED_CONTAINER = "lastFinishedContainer";
public static final String ID_LAST_FINISHED = "lastFinished";
public static final String ID_LAST_FINISHED_AGO = "lastFinishedAgo";
public static final String ID_NEXT_RUN_CONTAINER = "nextRunContainer";
public static final String ID_NEXT_RUN = "nextRun";
public static final String ID_NEXT_RUN_IN = "nextRunIn";
public static final String ID_NEXT_RETRY_CONTAINER = "nextRetryContainer";
public static final String ID_NEXT_RETRY = "nextRetry";
public static final String ID_NEXT_RETRY_IN = "nextRetryIn";
public static final String ID_SCHEDULING_TABLE = "schedulingTable";
public static final String ID_RECURRING_CONTAINER = "recurringContainer";
public static final String ID_RECURRING_CHECK = "recurringCheck";
public static final String ID_SUSPEND_REQ_RECURRING = "suspendReqRecurring";
//public static final String ID_RECURRENT_TASKS_CONTAINER = "recurrentTasksContainer";
public static final String ID_BOUND_CONTAINER = "boundContainer";
public static final String ID_BOUND_HELP = "boundHelp";
public static final String ID_BOUND_CHECK = "boundCheck";
public static final String ID_SUSPEND_REQ_BOUND = "suspendReqBound";
public static final String ID_INTERVAL_CONTAINER = "intervalContainer";
public static final String ID_CRON_CONTAINER = "cronContainer";
public static final String ID_INTERVAL = "interval";
public static final String ID_CRON = "cron";
public static final String ID_CRON_HELP = "cronHelp";
public static final String ID_NOT_START_BEFORE_CONTAINER = "notStartBeforeContainer";
public static final String ID_NOT_START_BEFORE_FIELD = "notStartBeforeField";
public static final String ID_NOT_START_AFTER_CONTAINER = "notStartAfterContainer";
public static final String ID_NOT_START_AFTER_FIELD = "notStartAfterField";
public static final String ID_MISFIRE_ACTION_CONTAINER = "misfireActionContainer";
public static final String ID_MISFIRE_ACTION = "misfireAction";
public static final String ID_THREAD_STOP_CONTAINER = "threadStopContainer";
public static final String ID_THREAD_STOP = "threadStop";
private PageTaskEdit parentPage;
private IModel<TaskDto> taskDtoModel;
public TaskSchedulingTabPanel(String id, Form mainForm,
LoadableModel<ObjectWrapper<TaskType>> taskWrapperModel,
IModel<TaskDto> taskDtoModel, PageTaskEdit parentPage) {
super(id, mainForm, taskWrapperModel, parentPage);
this.taskDtoModel = taskDtoModel;
this.parentPage = parentPage;
initLayoutForInfoPanel();
initLayoutForSchedulingTable();
setOutputMarkupId(true);
}
private void initLayoutForInfoPanel() {
// last start
WebMarkupContainer lastStartedContainer = new WebMarkupContainer(ID_LAST_STARTED_CONTAINER);
Label lastStart = new Label(ID_LAST_STARTED, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
TaskDto dto = taskDtoModel.getObject();
if (dto.getLastRunStartTimestampLong() == null) {
return "-";
} else {
return WebComponentUtil.formatDate(new Date(dto.getLastRunStartTimestampLong()));
}
}
});
lastStartedContainer.add(lastStart);
Label lastStartAgo = new Label(ID_LAST_STARTED_AGO, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
TaskDto dto = taskDtoModel.getObject();
if (dto.getLastRunStartTimestampLong() == null) {
return "";
} else {
final long ago = System.currentTimeMillis() - dto.getLastRunStartTimestampLong();
return createStringResource("TaskStatePanel.message.ago", DurationFormatUtils.formatDurationWords(ago, true, true)).getString();
}
}
});
lastStartedContainer.add(lastStartAgo);
lastStartedContainer.add(parentPage.createVisibleIfAccessible(TaskType.F_LAST_RUN_START_TIMESTAMP));
add(lastStartedContainer);
// last finish
WebMarkupContainer lastFinishedContainer = new WebMarkupContainer(ID_LAST_FINISHED_CONTAINER);
Label lastFinished = new Label(ID_LAST_FINISHED, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
TaskDto dto = taskDtoModel.getObject();
if (dto.getLastRunFinishTimestampLong() == null) {
return "-";
} else {
return WebComponentUtil.formatDate(new Date(dto.getLastRunFinishTimestampLong()));
}
}
});
lastFinishedContainer.add(lastFinished);
Label lastFinishedAgo = new Label(ID_LAST_FINISHED_AGO, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
TaskDto dto = taskDtoModel.getObject();
if (dto.getLastRunFinishTimestampLong() == null) {
return "";
} else {
Long duration;
if (dto.getLastRunStartTimestampLong() == null || dto.getLastRunFinishTimestampLong() < dto.getLastRunStartTimestampLong()) {
duration = null;
} else {
duration = dto.getLastRunFinishTimestampLong() - dto.getLastRunStartTimestampLong();
}
long ago = System.currentTimeMillis() - dto.getLastRunFinishTimestampLong();
if (duration != null) {
return getString("TaskStatePanel.message.durationAndAgo",
DurationFormatUtils.formatDurationWords(ago, true, true),
duration);
} else {
return getString("TaskStatePanel.message.ago",
DurationFormatUtils.formatDurationWords(ago, true, true));
}
}
}
});
lastFinishedContainer.add(lastFinishedAgo);
lastFinishedContainer.add(parentPage.createVisibleIfAccessible(TaskType.F_LAST_RUN_FINISH_TIMESTAMP));
add(lastFinishedContainer);
WebMarkupContainer nextRunContainer = new WebMarkupContainer(ID_NEXT_RUN_CONTAINER);
Label nextRun = new Label(ID_NEXT_RUN, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
TaskDto dto = taskDtoModel.getObject();
if (dto.isRecurring() && dto.isBound() && dto.isRunning()) {
return getString("pageTasks.runsContinually");
} else if (dto.getNextRunStartTimeLong() == null) {
return "-";
} else {
return WebComponentUtil.formatDate(new Date(dto.getNextRunStartTimeLong()));
}
}
});
nextRunContainer.add(nextRun);
Label nextRunIn = new Label(ID_NEXT_RUN_IN, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
TaskDto dto = taskDtoModel.getObject();
if (dto.getNextRunStartTimeLong() == null || (dto.isRecurring() && dto.isBound() && dto.isRunning())) {
return "";
} else {
long currentTime = System.currentTimeMillis();
final long in = dto.getNextRunStartTimeLong() - currentTime;
if (in >= 0) {
return getString("TaskStatePanel.message.in", DurationFormatUtils.formatDurationWords(in, true, true));
} else {
return "";
}
}
}
});
nextRunContainer.add(nextRunIn);
nextRunContainer.add(parentPage.createVisibleIfAccessible(TaskType.F_NEXT_RUN_START_TIMESTAMP));
add(nextRunContainer);
WebMarkupContainer nextRetryContainer = new WebMarkupContainer(ID_NEXT_RETRY_CONTAINER);
Label nextRetry = new Label(ID_NEXT_RETRY, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
TaskDto dto = taskDtoModel.getObject();
if (dto.getNextRetryTimeLong() == null) {
return "-";
} else {
return WebComponentUtil.formatDate(new Date(dto.getNextRetryTimeLong()));
}
}
});
nextRetryContainer.add(nextRetry);
Label nextRetryIn = new Label(ID_NEXT_RETRY_IN, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
TaskDto dto = taskDtoModel.getObject();
if (dto.getNextRetryTimeLong() == null /* || (dto.isRecurring() && dto.isBound() && dto.isRunning()) */ ) {
return "";
} else {
long currentTime = System.currentTimeMillis();
final long in = dto.getNextRetryTimeLong() - currentTime;
if (in >= 0) {
return getString("TaskStatePanel.message.in", DurationFormatUtils.formatDurationWords(in, true, true));
} else {
return "";
}
}
}
});
nextRetryContainer.add(nextRetryIn);
nextRetryContainer.add(new VisibleEnableBehaviour() {
@Override
public boolean isVisible() {
return taskDtoModel.getObject().getNextRetryTimeLong() != null;
}
});
add(nextRetryContainer);
}
private void initLayoutForSchedulingTable() {
// models
final IModel<Boolean> recurringCheckModel = new PropertyModel<>(taskDtoModel, TaskDto.F_RECURRING);
final IModel<Boolean> boundCheckModel = new PropertyModel<Boolean>(taskDtoModel, TaskDto.F_BOUND);
// behaviors
final VisibleEnableBehaviour visibleIfEditAndRunnableOrRunning = new VisibleEnableBehaviour() {
@Override
public boolean isVisible() {
return parentPage.isEdit() && parentPage.getTaskDto().isRunnableOrRunning();
}
};
final VisibleEnableBehaviour visibleIfRecurringAndScheduleIsAccessible = new VisibleEnableBehaviour() {
@Override
public boolean isVisible() {
return recurringCheckModel.getObject() && parentPage.isReadable(new ItemPath(TaskType.F_SCHEDULE));
}
};
final VisibleEnableBehaviour visibleIfRecurringAndLooselyBoundAndScheduleIsAccessible = new VisibleEnableBehaviour() {
@Override
public boolean isVisible() {
return recurringCheckModel.getObject() && !boundCheckModel.getObject() && parentPage.isReadable(new ItemPath(TaskType.F_SCHEDULE));
}
};
final VisibleEnableBehaviour enabledIfEditAndNotRunningRunnableOrLooselyBoundAndScheduleIsEditable = new VisibleEnableBehaviour() {
@Override
public boolean isEnabled() {
return parentPage.isEdit() && (!parentPage.getTaskDto().isRunnableOrRunning() || !boundCheckModel.getObject())
&& parentPage.isEditable(new ItemPath(TaskType.F_SCHEDULE));
}
};
final VisibleEnableBehaviour enabledIfEditAndNotRunningAndScheduleIsEditable = new VisibleEnableBehaviour() {
@Override
public boolean isEnabled() {
return parentPage.isEdit() && !parentPage.getTaskDto().isRunning() && parentPage.isEditable(new ItemPath(TaskType.F_SCHEDULE));
}
};
final VisibleEnableBehaviour enabledIfEditAndScheduleIsEditable = new VisibleEnableBehaviour() {
@Override
public boolean isEnabled() {
return parentPage.isEdit() && parentPage.isEditable(new ItemPath(TaskType.F_SCHEDULE));
}
};
final VisibleEnableBehaviour enabledIfEditAndThreadStopIsEditable = new VisibleEnableBehaviour() {
@Override
public boolean isEnabled() {
return parentPage.isEdit() && parentPage.isEditable(new ItemPath(TaskType.F_THREAD_STOP_ACTION));
}
};
// components
final WebMarkupContainer schedulingTable = new WebMarkupContainer(ID_SCHEDULING_TABLE);
schedulingTable.setOutputMarkupId(true);
add(schedulingTable);
WebMarkupContainer recurringContainer = new WebMarkupContainer(ID_RECURRING_CONTAINER);
AjaxCheckBox recurringCheck = new AjaxCheckBox(ID_RECURRING_CHECK, recurringCheckModel) {
@Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(schedulingTable);
}
};
recurringCheck.setOutputMarkupId(true);
recurringCheck.add(new VisibleEnableBehaviour() {
@Override
public boolean isEnabled() {
return parentPage.isEdit() && !parentPage.getTaskDto().isRunnableOrRunning() && parentPage.isEditable(TaskType.F_RECURRENCE);
}
});
recurringContainer.add(recurringCheck);
WebMarkupContainer suspendReqRecurring = new WebMarkupContainer(ID_SUSPEND_REQ_RECURRING);
suspendReqRecurring.add(visibleIfEditAndRunnableOrRunning);
recurringContainer.add(suspendReqRecurring);
recurringContainer.add(parentPage.createVisibleIfAccessible(TaskType.F_RECURRENCE));
schedulingTable.add(recurringContainer);
final WebMarkupContainer boundContainer = new WebMarkupContainer(ID_BOUND_CONTAINER);
boundContainer.setOutputMarkupId(true);
final AjaxCheckBox bound = new AjaxCheckBox(ID_BOUND_CHECK, boundCheckModel) {
@Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(schedulingTable);
}
};
bound.add(new VisibleEnableBehaviour() {
@Override
public boolean isEnabled() {
return parentPage.isEdit() && !parentPage.getTaskDto().isRunnableOrRunning() && parentPage.isEditable(TaskType.F_BINDING);
}
});
boundContainer.add(bound);
WebMarkupContainer suspendReqBound = new WebMarkupContainer(ID_SUSPEND_REQ_BOUND);
suspendReqBound.add(visibleIfEditAndRunnableOrRunning);
boundContainer.add(suspendReqBound);
boundContainer.add(new VisibleEnableBehaviour() {
@Override
public boolean isVisible() {
return recurringCheckModel.getObject() && parentPage.isReadable(new ItemPath(TaskType.F_BINDING));
}
});
Label boundHelp = new Label(ID_BOUND_HELP);
boundHelp.add(new InfoTooltipBehavior());
boundContainer.add(boundHelp);
schedulingTable.add(boundContainer);
WebMarkupContainer intervalContainer = new WebMarkupContainer(ID_INTERVAL_CONTAINER);
intervalContainer.add(visibleIfRecurringAndScheduleIsAccessible);
intervalContainer.setOutputMarkupId(true);
schedulingTable.add(intervalContainer);
TextField<Integer> interval = new TextField<>(ID_INTERVAL, new PropertyModel<Integer>(taskDtoModel, TaskDto.F_INTERVAL));
interval.add(new EmptyOnBlurAjaxFormUpdatingBehaviour());
interval.add(enabledIfEditAndNotRunningRunnableOrLooselyBoundAndScheduleIsEditable);
intervalContainer.add(interval);
WebMarkupContainer cronContainer = new WebMarkupContainer(ID_CRON_CONTAINER);
cronContainer.add(visibleIfRecurringAndLooselyBoundAndScheduleIsAccessible);
cronContainer.setOutputMarkupId(true);
schedulingTable.add(cronContainer);
TextField<String> cron = new TextField<>(ID_CRON, new PropertyModel<String>(taskDtoModel, TaskDto.F_CRON_SPECIFICATION));
cron.add(new EmptyOnBlurAjaxFormUpdatingBehaviour());
cron.add(enabledIfEditAndNotRunningRunnableOrLooselyBoundAndScheduleIsEditable);
cronContainer.add(cron);
Label cronHelp = new Label(ID_CRON_HELP);
cronHelp.add(new InfoTooltipBehavior());
cronContainer.add(cronHelp);
WebMarkupContainer notStartBeforeContainer = new WebMarkupContainer(ID_NOT_START_BEFORE_CONTAINER);
DateInput notStartBefore = new DateInput(ID_NOT_START_BEFORE_FIELD, new PropertyModel<Date>(taskDtoModel, TaskDto.F_NOT_START_BEFORE));
notStartBefore.setOutputMarkupId(true);
notStartBefore.add(enabledIfEditAndNotRunningAndScheduleIsEditable);
notStartBeforeContainer.add(notStartBefore);
notStartBeforeContainer.add(parentPage.createVisibleIfAccessible(TaskType.F_SCHEDULE));
schedulingTable.add(notStartBeforeContainer);
WebMarkupContainer notStartAfterContainer = new WebMarkupContainer(ID_NOT_START_AFTER_CONTAINER);
DateInput notStartAfter = new DateInput(ID_NOT_START_AFTER_FIELD, new PropertyModel<Date>(taskDtoModel, TaskDto.F_NOT_START_AFTER));
notStartAfter.setOutputMarkupId(true);
notStartAfter.add(enabledIfEditAndNotRunningAndScheduleIsEditable);
notStartAfterContainer.add(notStartAfter);
notStartAfterContainer.add(parentPage.createVisibleIfAccessible(TaskType.F_SCHEDULE));
schedulingTable.add(notStartAfterContainer);
WebMarkupContainer misfireActionContainer = new WebMarkupContainer(ID_MISFIRE_ACTION_CONTAINER);
DropDownChoice misfire = new DropDownChoice(ID_MISFIRE_ACTION,
new PropertyModel<MisfireActionType>(taskDtoModel, TaskDto.F_MISFIRE_ACTION),
WebComponentUtil.createReadonlyModelFromEnum(MisfireActionType.class),
new EnumChoiceRenderer<MisfireActionType>(parentPage));
misfire.add(enabledIfEditAndScheduleIsEditable);
misfireActionContainer.add(misfire);
misfireActionContainer.add(parentPage.createVisibleIfAccessible(TaskType.F_SCHEDULE));
schedulingTable.add(misfireActionContainer);
WebMarkupContainer threadStopContainer = new WebMarkupContainer(ID_THREAD_STOP_CONTAINER);
DropDownChoice threadStop = new DropDownChoice<>(ID_THREAD_STOP, new Model<ThreadStopActionType>() {
@Override
public ThreadStopActionType getObject() {
return taskDtoModel.getObject().getThreadStopActionType();
}
@Override
public void setObject(ThreadStopActionType object) {
taskDtoModel.getObject().setThreadStopActionType(object);
}
}, WebComponentUtil.createReadonlyModelFromEnum(ThreadStopActionType.class),
new EnumChoiceRenderer<ThreadStopActionType>(parentPage));
threadStop.add(enabledIfEditAndThreadStopIsEditable);
threadStopContainer.add(threadStop);
threadStopContainer.add(parentPage.createVisibleIfAccessible(TaskType.F_THREAD_STOP_ACTION));
schedulingTable.add(threadStopContainer);
org.apache.wicket.markup.html.form.Form<?> form = parentPage.getForm();
// if not removed, the validators will accumulate on the form
// TODO implement more intelligently when other tabs have validators as well
for (IFormValidator validator : form.getFormValidators()) {
form.remove(validator);
}
form.add(new StartEndDateValidator(notStartBefore, notStartAfter));
form.add(new ScheduleValidator(parentPage.getTaskManager(), recurringCheck, bound, interval, cron));
}
@Override
public Collection<Component> getComponentsToUpdate() {
return Collections.<Component>singleton(this);
}
}