![JAR search and dependency download from the Maven repository](/logo.png)
org.ikasan.dashboard.ui.scheduler.component.ScheduledJobDialog Maven / Gradle / Ivy
package org.ikasan.dashboard.ui.scheduler.component;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.ItemLabelGenerator;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.timepicker.TimePicker;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.data.converter.StringToLongConverter;
import org.ikasan.dashboard.ui.general.component.AbstractCloseableResizableDialog;
import org.ikasan.dashboard.ui.general.component.NotificationHelper;
import org.ikasan.dashboard.ui.scheduler.util.ScheduledProcessConstants;
import org.ikasan.dashboard.ui.util.DateTimeUtil;
import org.ikasan.dashboard.ui.util.IconDecorator;
import org.ikasan.dashboard.ui.util.SystemEventConstants;
import org.ikasan.dashboard.ui.util.SystemEventLogger;
import org.ikasan.scheduled.event.model.ScheduledProcessAggregateConfiguration;
import org.ikasan.scheduled.event.model.ScheduledProcessConfigurationConstants;
import org.ikasan.scheduled.event.service.ScheduledProcessManagementService;
import org.ikasan.security.service.authentication.IkasanAuthentication;
import org.ikasan.spec.metadata.ConfigurationMetaData;
import org.ikasan.spec.metadata.ConfigurationParameterMetaData;
import org.ikasan.spec.metadata.ModuleMetaData;
import org.ikasan.spec.module.client.ConfigurationService;
import org.ikasan.spec.module.client.MetaDataService;
import org.ikasan.spec.module.client.ModuleControlService;
import org.quartz.CronExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.vaadin.miki.shared.dates.DatePatterns;
import org.vaadin.miki.superfields.dates.SuperDatePicker;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class ScheduledJobDialog extends AbstractCloseableResizableDialog {
Logger logger = LoggerFactory.getLogger(ScheduledJobDialog.class);
private ComboBox agentCb;
private Checkbox startAutomaticCb;
// Fields to capture schedule job properties.
private TextField jobNameTf;
private TextField jobGroupTf;
private TextArea jobDescriptionTa;
private TextField cronExpressionTf;
private ComboBox timezoneCb;
private List passThroughProperties;
// Fields to capture job execution properties.
private TextArea commandLineTa;
private TextField workingDirectoryTf;
private TextField secondsToWaitForProcessStartTf;
private TextField stdOutTf;
private TextField stdErrTf;
private Checkbox retryOnFailCb;
private List successfulReturnCodes;
// Fields to capture blackout router properties.
private Button addBlackoutCron;
private List blackoutCronExpressions;
private Button addDateTimeRange;
private List dateTimeRanges;
private Button saveButton;
private Button cancelButton;
private Button successfulReturnCodesButton;
private Button passThroughPropertiesButton;
private Label noBlackOutCronExpressionLabel;
private Label noBlackOutDateTimeRangesLabel;
private Label noPassThoughPropertiesLabel;
private Label noReturnCodesLabel;
private Label passThroughPropertiesLabel;
private Label successfulReturnCodesLabel;
private Label blackOutCronExpressionLabel;
private Div passThroughPropertiesDiv;
private Label blackOutCronExpressionDiv;
private Div returnCodesDiv;
private ScheduledProcessManagementService scheduledProcessManagementService;
private ConfigurationService configurationRestService;
private ModuleMetaData agent;
private ModuleControlService moduleControlRestService;
private MetaDataService metaDataRestService;
private ScheduledProcessAggregateConfiguration scheduleProcessAggregateConfiguration = new ScheduledProcessAggregateConfiguration();
private ScheduledProcessAggregateConfiguration oldScheduleProcessAggregateConfiguration;
private Binder formBinder;
private EditMode editMode = EditMode.NEW;
private FormLayout formLayout;
private boolean enabled = true;
private SystemEventLogger systemEventLogger;
/**
* Constructor
*
* @param agent
* @param scheduledProcessManagementService
* @param configurationRestService
* @param moduleControlRestService
* @param metaDataRestService
* @param systemEventLogger
*/
public ScheduledJobDialog(ModuleMetaData agent, ScheduledProcessManagementService scheduledProcessManagementService,
ConfigurationService configurationRestService, ModuleControlService moduleControlRestService,
MetaDataService metaDataRestService, SystemEventLogger systemEventLogger) {
super.showResize(false);
super.title.setText("Scheduled Job");
this.agent = agent;
this.scheduledProcessManagementService = scheduledProcessManagementService;
this.configurationRestService = configurationRestService;
this.moduleControlRestService = moduleControlRestService;
this.metaDataRestService = metaDataRestService;
this.systemEventLogger = systemEventLogger;
this.noBlackOutCronExpressionLabel = new Label(getTranslation("label.no-blackout-cron-expressions", UI.getCurrent().getLocale()));
this.noBlackOutCronExpressionLabel.setVisible(false);
this.noBlackOutCronExpressionLabel.getStyle().set("color", "rgba(0, 0, 0, 0.38)");
this.noBlackOutDateTimeRangesLabel = new Label(getTranslation("label.no-blackout-date-time-ranges", UI.getCurrent().getLocale()));
this.noBlackOutDateTimeRangesLabel.setVisible(false);
this.noBlackOutDateTimeRangesLabel.getStyle().set("color", "rgba(0, 0, 0, 0.38)");
this.noBlackOutDateTimeRangesLabel.getStyle().set("padding-bottom", "20px");
this.noPassThoughPropertiesLabel = new Label(getTranslation("label.no-pass-through-properties", UI.getCurrent().getLocale()));
this.noPassThoughPropertiesLabel.setVisible(false);
this.noPassThoughPropertiesLabel.getStyle().set("color", "rgba(0, 0, 0, 0.38)");
this.noReturnCodesLabel = new Label(getTranslation("label.no-return-codes", UI.getCurrent().getLocale()));
this.noReturnCodesLabel.setVisible(false);
this.noReturnCodesLabel.getStyle().set("color", "rgba(0, 0, 0, 0.38)");
this.formBinder
= new Binder<>(ScheduledProcessAggregateConfiguration.class);
this.blackoutCronExpressions = new ArrayList<>();
this.dateTimeRanges = new ArrayList<>();
this.passThroughProperties = new ArrayList<>();
this.successfulReturnCodes = new ArrayList<>();
this.setHeight("900px");
this.setWidth("1200px");
saveButton = new Button(getTranslation("button.save", UI.getCurrent().getLocale()));
saveButton.setId("scheduledJobSaveButton");
saveButton.addClickListener((ComponentEventListener>) buttonClickEvent -> {
if(!this.performFormValidation(this.scheduleProcessAggregateConfiguration)) {
NotificationHelper.showErrorNotification(getTranslation("error.scheduled-job-configuration", UI.getCurrent().getLocale()));
return;
}
try {
createOrUpdateScheduledJob(this.scheduleProcessAggregateConfiguration);
}
catch (Exception e) {
e.printStackTrace();
NotificationHelper.showErrorNotification(getTranslation("error.scheduled-job-creation", UI.getCurrent().getLocale()));
return;
}
IkasanAuthentication authentication = (IkasanAuthentication) SecurityContextHolder.getContext().getAuthentication();
if (this.editMode == EditMode.NEW || this.editMode == EditMode.CLONE) {
String action = String.format("New scheduled job created [%s].", this.scheduleProcessAggregateConfiguration);
this.systemEventLogger.logEvent(SystemEventConstants.NEW_SCHEDULED_JOB_CREATED, action, authentication.getName());
}
else if (this.editMode == EditMode.EDIT) {
String action = String.format("Scheduled job edited. \nBefore [%s]\nAfter [%s].", this.oldScheduleProcessAggregateConfiguration,
this.scheduleProcessAggregateConfiguration);
this.systemEventLogger.logEvent(SystemEventConstants.SCHEDULED_JOB_EDIT, action, authentication.getName());
}
this.close();
});
cancelButton = new Button(getTranslation("button.cancel", UI.getCurrent().getLocale()));
cancelButton.addClickListener((ComponentEventListener>) buttonClickEvent -> this.close());
HorizontalLayout buttonLayout = new HorizontalLayout();
buttonLayout.setMargin(true);
buttonLayout.setSpacing(true);
buttonLayout.add(saveButton, cancelButton);
buttonLayout.getStyle().set("padding-bottom", "20px");
VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
layout.add(this.createConfigurationForm(), buttonLayout);
layout.setHorizontalComponentAlignment(FlexComponent.Alignment.CENTER, buttonLayout);
layout.getStyle().set("padding-bottom", "20px");
super.content.add(layout);
}
/**
* Initialise the form.
*
* @return
*/
private FormLayout createConfigurationForm() {
formLayout = new FormLayout();
this.agentCb = new ComboBox<>(getTranslation("label.agent", UI.getCurrent().getLocale()));
this.agentCb.setId("agentCb");
this.agentCb.setRequired(true);
this.agentCb.setClearButtonVisible(true);
this.agentCb.setItems(this.scheduledProcessManagementService.getAllAgentNames());
if(agent != null) {
this.agentCb.setValue(agent.getName());
this.agentCb.setEnabled(false);
}
formBinder.forField(this.agentCb)
.withValidator(agentValue -> !agentValue.isEmpty(), getTranslation("error.missing-agent", UI.getCurrent().getLocale()))
.bind(ScheduledProcessAggregateConfiguration::getAgentName, ScheduledProcessAggregateConfiguration::setAgentName);
formLayout.add(agentCb, 2);
this.startAutomaticCb = new Checkbox(getTranslation("label.start-automatically", UI.getCurrent().getLocale()));
this.startAutomaticCb.setId("startAutomaticCb");
this.startAutomaticCb.setValue(true);
formBinder.forField(this.startAutomaticCb)
.bind(ScheduledProcessAggregateConfiguration::isStartAutomatically, ScheduledProcessAggregateConfiguration::setStartAutomatically);
formLayout.add(this.startAutomaticCb);
// Fields to capture schedule job properties.
H3 scheduleDetailsLabel = new H3(getTranslation("header.schedule-details", UI.getCurrent().getLocale()));
formLayout.add(scheduleDetailsLabel, 2);
this.jobNameTf = new TextField(getTranslation("label.job-name", UI.getCurrent().getLocale()));
this.jobNameTf.setId("jobNameTf");
this.jobNameTf.setRequired(true);
this.jobNameTf.setEnabled(this.editMode == EditMode.NEW || this.editMode == EditMode.CLONE);
formBinder.forField(this.jobNameTf)
.withValidator(jobName -> !jobName.isEmpty(), getTranslation("error.missing-job-name", UI.getCurrent().getLocale()))
.bind(ScheduledProcessAggregateConfiguration::getJobName, ScheduledProcessAggregateConfiguration::setJobName);
formLayout.add(jobNameTf);
this.jobGroupTf = new TextField(getTranslation("label.job-group", UI.getCurrent().getLocale()));
this.jobGroupTf.setRequired(true);
this.jobGroupTf.setId("jobGroupTf");
formBinder.forField(this.jobGroupTf)
.withValidator(jobGroup -> !jobGroup.isEmpty(), getTranslation("error.missing-job-group", UI.getCurrent().getLocale()))
.bind(ScheduledProcessAggregateConfiguration::getJobGroup, ScheduledProcessAggregateConfiguration::setJobGroup);
formLayout.add(jobGroupTf);
this.jobDescriptionTa = new TextArea(getTranslation("label.job-description", UI.getCurrent().getLocale()));
this.jobDescriptionTa.setRequired(true);
this.jobDescriptionTa.setId("jobDescriptionTa");
jobDescriptionTa.getStyle().set("minHeight", "100px");
formBinder.forField(this.jobDescriptionTa)
.withValidator(jobGroup -> !jobGroup.isEmpty(), getTranslation("error.missing-job-description", UI.getCurrent().getLocale()))
.bind(ScheduledProcessAggregateConfiguration::getJobDescription, ScheduledProcessAggregateConfiguration::setJobDescription);
formLayout.add(jobDescriptionTa, 2);
Icon builderIcon = IconDecorator.decorate(VaadinIcon.BUILDING_O.create(), "Build cron expression", "14pt", "rgba(241, 90, 35, 1.0)");
builderIcon.addClickListener(event -> {
CronBuilderDialog dialog = new CronBuilderDialog();
dialog.init(this.cronExpressionTf.getValue());
dialog.open();
dialog.addOpenedChangeListener(openedChangeEvent -> {
if(!openedChangeEvent.isOpened() && dialog.isSaveClose()) {
this.cronExpressionTf.setValue(dialog.getCronExpression());
}
});
});
this.cronExpressionTf = new TextField(getTranslation("label.cron-expression", UI.getCurrent().getLocale()));
this.cronExpressionTf.setRequired(true);
this.cronExpressionTf.setId("cronExpressionTf");
this.cronExpressionTf.setSuffixComponent(builderIcon);
formBinder.forField(this.cronExpressionTf)
.withValidator(value -> !value.isEmpty(), getTranslation("error.missing-cron-expression", UI.getCurrent().getLocale()))
.withValidator(value -> CronExpression.isValidExpression(value), getTranslation("error.invalid-cron-expression", UI.getCurrent().getLocale()))
.bind(ScheduledProcessAggregateConfiguration::getCronExpression, ScheduledProcessAggregateConfiguration::setCronExpression);
formLayout.add(cronExpressionTf);
this.timezoneCb = new ComboBox<>(getTranslation("label.timezone", UI.getCurrent().getLocale()));
ComboBox.ItemFilter filter = (element, filterString) ->
element.zoneId.toLowerCase().contains(filterString.toLowerCase());
this.timezoneCb.setId("timezoneCb");
this.timezoneCb.setItems(filter, DateTimeUtil.getAllZoneIdsAndItsOffSet());
this.timezoneCb.setItemLabelGenerator((ItemLabelGenerator) s -> String.format("%35s (UTC%s) %n", s.zoneId, s.offset).trim());
this.timezoneCb.setClearButtonVisible(true);
this.timezoneCb.setPlaceholder(getTranslation("label.choose-a-timezone", UI.getCurrent().getLocale()));
this.timezoneCb.setErrorMessage(getTranslation("error.timezone-required", UI.getCurrent().getLocale()));
formLayout.add(timezoneCb);
this.passThroughPropertiesLabel = new Label(getTranslation("label.pass-through-properties", UI.getCurrent().getLocale()));
this.passThroughPropertiesButton = new Button(VaadinIcon.PLUS.create(), e -> {
this.addPassThroughProperties(null, null);
});
this.passThroughPropertiesButton.setId("passThroughPropertiesButton");
this.passThroughPropertiesDiv = new Div();
this.passThroughPropertiesDiv.setVisible(false);
formLayout.add(passThroughPropertiesLabel, passThroughPropertiesButton, noPassThoughPropertiesLabel, passThroughPropertiesDiv);
// Fields to capture job execution properties.
H3 jobExecutionLabel = new H3(getTranslation("header.job-execution-details", UI.getCurrent().getLocale()));
formLayout.add(jobExecutionLabel, 2);
this.commandLineTa = new TextArea(getTranslation("label.command-line", UI.getCurrent().getLocale()));
this.commandLineTa.setId("commandLineTa");
this.commandLineTa.setRequired(true);
formBinder.forField(this.commandLineTa)
.withValidator(value -> !value.isEmpty(), getTranslation("error.command-line-missing", UI.getCurrent().getLocale()))
.bind(ScheduledProcessAggregateConfiguration::getCommandLine, ScheduledProcessAggregateConfiguration::setCommandLine);
formLayout.add(commandLineTa, 2);
commandLineTa.getStyle().set("minHeight", "100px");
this.workingDirectoryTf = new TextField(getTranslation("label.working-directory", UI.getCurrent().getLocale()));
formBinder.forField(this.workingDirectoryTf)
.withNullRepresentation("")
.bind(ScheduledProcessAggregateConfiguration::getWorkingDirectory, ScheduledProcessAggregateConfiguration::setWorkingDirectory);
formLayout.add(workingDirectoryTf);
this.secondsToWaitForProcessStartTf = new TextField(getTranslation("label.seconds-to-wait", UI.getCurrent().getLocale()));
formBinder.forField(this.secondsToWaitForProcessStartTf)
.withNullRepresentation("")
.withConverter(new StringToLongConverter(getTranslation("error.must-be-a-number", UI.getCurrent().getLocale())))
.bind(ScheduledProcessAggregateConfiguration::getSecondsToWaitForProcessStart, ScheduledProcessAggregateConfiguration::setSecondsToWaitForProcessStart);
formLayout.add(secondsToWaitForProcessStartTf);
this.stdOutTf = new TextField(getTranslation("label.std-out", UI.getCurrent().getLocale()));
this.stdOutTf.setRequired(false);
this.stdOutTf.setId("stdOutTf");
formBinder.forField(this.stdOutTf)
.withNullRepresentation("")
.bind(ScheduledProcessAggregateConfiguration::getStdOut, ScheduledProcessAggregateConfiguration::setStdOut);
formLayout.add(stdOutTf);
this.stdErrTf = new TextField(getTranslation("label.std-err", UI.getCurrent().getLocale()));
this.stdErrTf.setRequired(false);
this.stdErrTf.setId("stdErrTf");
formBinder.forField(this.stdErrTf)
.withNullRepresentation("")
.bind(ScheduledProcessAggregateConfiguration::getStdErr, ScheduledProcessAggregateConfiguration::setStdErr);
formLayout.add(this.stdErrTf);
this.retryOnFailCb = new Checkbox(getTranslation("label.retry-on-fail", UI.getCurrent().getLocale()));
formBinder.forField(this.retryOnFailCb)
.withNullRepresentation(false)
.bind(ScheduledProcessAggregateConfiguration::isRetryOnFail, ScheduledProcessAggregateConfiguration::setRetryOnFail);
this.retryOnFailCb.getStyle().set("padding-top", "15px");
formLayout.add(this.retryOnFailCb, new Div());
this.successfulReturnCodesLabel = new Label(getTranslation("label.successful-return-codes", UI.getCurrent().getLocale()));
this.successfulReturnCodesButton = new Button(VaadinIcon.PLUS.create(), e -> {
this.addSuccessfulReturnCodes(null);
});
this.successfulReturnCodesButton.setId("successfulReturnCodesButton");
this.returnCodesDiv = new Div();
this.returnCodesDiv.setVisible(false);
formLayout.add(successfulReturnCodesLabel, successfulReturnCodesButton, this.noReturnCodesLabel, returnCodesDiv);
H3 blackoutLabel = new H3(getTranslation("header.blackout-execution-details", UI.getCurrent().getLocale()));
formLayout.add(blackoutLabel, 2);
blackOutCronExpressionLabel = new Label(getTranslation("label.blackout-cron-expressions", UI.getCurrent().getLocale()));
this.addBlackoutCron = new Button(VaadinIcon.PLUS.create(), e -> {
this.addBlackoutCronExpression(null);
});
this.addBlackoutCron.setId("addBlackoutCron");
this.blackOutCronExpressionDiv = new Label("");
this.blackOutCronExpressionDiv.setVisible(false);
formLayout.add(blackOutCronExpressionLabel, addBlackoutCron, this.noBlackOutCronExpressionLabel, blackOutCronExpressionDiv);
Label blackOutDateTimeRangesLabel = new Label(getTranslation("label.blackout-date-time-ranges", UI.getCurrent().getLocale()));
blackOutDateTimeRangesLabel.getStyle().set("padding-top", "30px");
this.addDateTimeRange = new Button(VaadinIcon.PLUS.create(), e -> {
this.addDateTimeRange(-1, -1);
});
this.addDateTimeRange.setId("addDateTimeRange");
formLayout.add(blackOutDateTimeRangesLabel, this.addDateTimeRange, this.noBlackOutDateTimeRangesLabel);
return formLayout;
}
/**
* Perform validation of the form.
*
* @param scheduleProcessAggregateConfiguration
* @return
*/
private boolean performFormValidation(ScheduledProcessAggregateConfiguration scheduleProcessAggregateConfiguration) {
try {
AtomicBoolean isValid = new AtomicBoolean(true);
scheduleProcessAggregateConfiguration.getBlackoutDateTimeRanges().clear();
this.dateTimeRanges.forEach(dateTimeRange -> {
if(dateTimeRange.startDate.getValue() == null){
dateTimeRange.startDate.setInvalid(true);
isValid.set(false);
}
if(dateTimeRange.startTime.getValue() == null){
dateTimeRange.startTime.setInvalid(true);
isValid.set(false);
}
if(dateTimeRange.endDate.getValue() == null){
dateTimeRange.endDate.setInvalid(true);
isValid.set(false);
}
if(dateTimeRange.endTime.getValue() == null){
dateTimeRange.endTime.setInvalid(true);
isValid.set(false);
}
if(isValid.get()) {
String startMilli = String.valueOf((dateTimeRange.startDate.getValue().atStartOfDay(DateTimeUtil.getZoneId()).toEpochSecond() * 1000) +
(dateTimeRange.startTime.getValue().toSecondOfDay()*1000));
String endMilli = String.valueOf((dateTimeRange.endDate.getValue().atStartOfDay(DateTimeUtil.getZoneId()).toEpochSecond() * 1000) +
(dateTimeRange.endTime.getValue().toSecondOfDay()*1000));
scheduleProcessAggregateConfiguration.getBlackoutDateTimeRanges().put(startMilli, endMilli);
}
});
scheduleProcessAggregateConfiguration.getBlackoutCronExpressions().clear();
this.blackoutCronExpressions.forEach(expression -> {
if(expression.getValue() == null || expression.getValue().isEmpty()) {
expression.setInvalid(true);
isValid.set(false);
} else if(!CronExpression.isValidExpression(expression.getValue())){
expression.setErrorMessage(getTranslation("error.invalid-cron-expression", UI.getCurrent().getLocale()));
expression.setInvalid(true);
isValid.set(false);
}
if(isValid.get()) {
scheduleProcessAggregateConfiguration.getBlackoutCronExpressions().add(expression.getValue());
}
});
scheduleProcessAggregateConfiguration.getSuccessfulReturnCodes().clear();
this.successfulReturnCodes.forEach(expression -> {
if(expression.getValue() == null || expression.getValue().isEmpty()) {
expression.setInvalid(true);
isValid.set(false);
}
else {
try {
Integer.parseInt(expression.getValue());
scheduleProcessAggregateConfiguration.getSuccessfulReturnCodes().add(expression.getValue());
}
catch (NumberFormatException e) {
expression.setErrorMessage(getTranslation("error.must-be-a-number", UI.getCurrent().getLocale()));
expression.setInvalid(true);
isValid.set(false);
}
}
});
scheduleProcessAggregateConfiguration.getPassthroughProperties().clear();
this.passThroughProperties.forEach(expression -> {
if(expression.nameTf.getValue() == null || expression.nameTf.getValue().isEmpty()) {
expression.nameTf.setInvalid(true);
isValid.set(false);
}
if(expression.valueTf.getValue() == null || expression.valueTf.getValue().isEmpty()) {
expression.valueTf.setInvalid(true);
isValid.set(false);
}
if(isValid.get()) {
scheduleProcessAggregateConfiguration.getPassthroughProperties().put(expression.nameTf.getValue(), expression.valueTf.getValue());
}
});
if(this.timezoneCb.getValue() != null) {
scheduleProcessAggregateConfiguration.setTimezone(this.timezoneCb.getValue().zoneId);
}
formBinder.writeBean(scheduleProcessAggregateConfiguration);
if(!isValid.get()){
return false;
}
}
catch (ValidationException e) {
return false;
}
return true;
}
/**
* This method interacts with with agent in order to create a new scheduler agent flow or update an existing flow and associated job.
*
* @param scheduleProcessAggregateConfiguration
*/
public void createOrUpdateScheduledJob(ScheduledProcessAggregateConfiguration scheduleProcessAggregateConfiguration) {
// Get the module configuration from the module.
ConfigurationMetaData> moduleConfiguration
= this.configurationRestService.getModuleConfiguration(this.agent.getUrl());
try {
if (moduleConfiguration == null) {
throw new RuntimeException(String.format("Could not find module configuration for agent[%s]", agent.getName()));
}
logger.debug("Module Configuration: " + moduleConfiguration);
if (this.editMode == EditMode.NEW || this.editMode == EditMode.CLONE) {
// Get the flowDefinitions from the configuration metadata.
moduleConfiguration.getParameters().stream()
.filter(configurationParameterMetaData -> configurationParameterMetaData.getName().equals("flowDefinitions"))
.findFirst().ifPresentOrElse(flowDefinitions -> {
// Add the new job flow to the map.
Map configurationMap = (Map) flowDefinitions.getValue();
configurationMap.put(scheduleProcessAggregateConfiguration.getJobName(), "MANUAL");
flowDefinitions.setValue(configurationMap);
logger.info("Module Configuration: " + moduleConfiguration);
// update the configuration back onto the module.
this.configurationRestService.storeConfiguration(this.agent.getUrl(), moduleConfiguration);
}, () -> {
throw new RuntimeException(String.format("Could not find flow definitions from module configuration for agent[%s]", agent));
});
// We need to deactivate and activate the module so the new flow is initialised
this.changeActivation("deactivate");
this.changeActivation("activate");
}
// Load the required configurations for a scheduled job.
Optional moduleMetaData = this.metaDataRestService.getModuleMetadata(agent.getUrl(), agent.getName());
ConfigurationMetaData> scheduledConsumerConfiguration = this.getConfigurationForAgentFlowComponent(moduleMetaData,
this.jobNameTf.getValue(), ScheduledProcessConstants.SCHEDULED_CONSUMER);
ConfigurationMetaData> blackoutRouterConfiguration = this.getConfigurationForAgentFlowComponent(moduleMetaData,
this.jobNameTf.getValue(), ScheduledProcessConstants.BLACKOUT_ROUTER);
ConfigurationMetaData> processExecutionBrokerConfiguration = this.getConfigurationForAgentFlowComponent(moduleMetaData,
this.jobNameTf.getValue(), ScheduledProcessConstants.PROCESS_EXECUTION_BROKER);
// Update all the configurations with the configurations provided in the form.
this.updateScheduleConsumerConfiguration(scheduledConsumerConfiguration, scheduleProcessAggregateConfiguration);
this.updateBlackoutRouterConfiguration(blackoutRouterConfiguration, scheduleProcessAggregateConfiguration);
this.updateProcessExecutionBrokerConfiguration(processExecutionBrokerConfiguration, scheduleProcessAggregateConfiguration);
// Save all the configurations back to the agent.
logger.debug(scheduledConsumerConfiguration.toString());
if(!this.configurationRestService.storeConfiguration(this.agent.getUrl(), scheduledConsumerConfiguration)) {
throw new RuntimeException(String.format("Could not store scheduled consumer configuration [%s]", scheduledConsumerConfiguration));
}
this.scheduledProcessManagementService.saveConfiguration(scheduledConsumerConfiguration);
logger.debug(blackoutRouterConfiguration.toString());
if(!this.configurationRestService.storeConfiguration(this.agent.getUrl(), blackoutRouterConfiguration)) {
throw new RuntimeException(String.format("Could not store blackout router configuration [%s]", blackoutRouterConfiguration));
}
this.scheduledProcessManagementService.saveConfiguration(blackoutRouterConfiguration);
logger.debug(processExecutionBrokerConfiguration.toString());
if(!this.configurationRestService.storeConfiguration(this.agent.getUrl(), processExecutionBrokerConfiguration)) {
throw new RuntimeException(String.format("Could not store process execution configuration [%s]", blackoutRouterConfiguration));
}
this.scheduledProcessManagementService.saveConfiguration(processExecutionBrokerConfiguration);
if(this.startAutomaticCb.getValue()) {
// Now that all configurations are applied we need to set up the startup type and restart the flow
String startupType = this.startAutomaticCb.getValue() ? "AUTOMATIC" : "MANUAL";
moduleConfiguration.getParameters().stream()
.filter(configurationParameterMetaData -> configurationParameterMetaData.getName().equals("flowDefinitions"))
.findFirst().ifPresentOrElse(flowDefinitions -> {
// Add the new job flow to the map.
Map configurationMap = (Map) flowDefinitions.getValue();
configurationMap.replace(scheduleProcessAggregateConfiguration.getJobName(), startupType);
flowDefinitions.setValue(configurationMap);
logger.info("Module Configuration: " + moduleConfiguration);
// update the configuration back onto the module.
this.configurationRestService.storeConfiguration(this.agent.getUrl(), moduleConfiguration);
}, () -> {
throw new RuntimeException(String.format("Could not find flow definitions from module configuration for agent[%s] " +
"when attempting to update start up control.", agent));
});
this.moduleControlRestService.changeFlowStartupType(this.agent.getUrl(), this.agent.getName(), scheduleProcessAggregateConfiguration.getJobName()
, startupType, "Scheduler flow requires automatic startup.");
}
// In order for the configuration to be applied the flow must be stopped and started.
this.moduleControlRestService.changeFlowState(this.agent.getUrl(), this.agent.getName(), scheduleProcessAggregateConfiguration.getJobName(), "stop");
this.moduleControlRestService.changeFlowState(this.agent.getUrl(), this.agent.getName(), scheduleProcessAggregateConfiguration.getJobName(), "start");
}
catch (Exception e) {
// If any exceptions occur we are going to remove the job that we attempted to create.
if(moduleConfiguration != null) {
moduleConfiguration.getParameters().stream()
.filter(configurationParameterMetaData -> configurationParameterMetaData.getName().equals("flowDefinitions"))
.findFirst().ifPresentOrElse(flowDefinitions -> {
// Add the new job flow to the map.
Map configurationMap = (Map) flowDefinitions.getValue();
configurationMap.remove(scheduleProcessAggregateConfiguration.getJobName());
flowDefinitions.setValue(configurationMap);
logger.info("Module Configuration: " + moduleConfiguration);
// update the configuration back onto the module.
this.configurationRestService.storeConfiguration(this.agent.getUrl(), moduleConfiguration);
}, () -> {
throw new RuntimeException(String.format("Could not find flow definitions from module configuration for agent[%s]", agent));
});
// We need to deactivate and activate the module so the new flow is removed when initialisation occurs.
this.changeActivation("deactivate");
this.changeActivation("activate");
}
throw e;
}
}
/**
* Helper method to call activation endpoint on the scheduler agent.
*
* @param action
*/
private void changeActivation(String action) {
boolean success = this.moduleControlRestService.changeModuleActivationState(this.agent.getUrl(), this.agent.getName(), action);
if (!success) {
throw new RuntimeException(String.format("Could not %s agent[%s]", action, agent));
}
}
/**
* Update the scheduled consumer configuration.
*
* @param scheduledConsumerConfiguration
* @param scheduleProcessAggregateConfiguration
*/
private void updateScheduleConsumerConfiguration(ConfigurationMetaData> scheduledConsumerConfiguration
, ScheduledProcessAggregateConfiguration scheduleProcessAggregateConfiguration) {
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.JOB_NAME,
scheduleProcessAggregateConfiguration.getJobName());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.JOB_GROUP_NAME,
scheduleProcessAggregateConfiguration.getJobGroup());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.JOB_DESCRIPTION,
scheduleProcessAggregateConfiguration.getJobDescription());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.CRON_EXPRESSION,
scheduleProcessAggregateConfiguration.getCronExpression());
if(scheduleProcessAggregateConfiguration.getTimezone() != null) {
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.TIMEZONE,
scheduleProcessAggregateConfiguration.getTimezone());
}
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.PASS_THROUGH_PROPERTIES,
scheduleProcessAggregateConfiguration.getPassthroughProperties());
}
/**
* Update the execution broker configuration.
*
* @param scheduledConsumerConfiguration
* @param scheduleProcessAggregateConfiguration
*/
private void updateProcessExecutionBrokerConfiguration(ConfigurationMetaData> scheduledConsumerConfiguration
, ScheduledProcessAggregateConfiguration scheduleProcessAggregateConfiguration) {
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.COMMAND_LINE,
scheduleProcessAggregateConfiguration.getCommandLine());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.WORKING_DIRECTORY,
scheduleProcessAggregateConfiguration.getWorkingDirectory());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.SUCCESSFUL_RETURN_CODES,
scheduleProcessAggregateConfiguration.getSuccessfulReturnCodes());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.SECONDS_TO_WAIT_FOR_PROCESS_TO_START,
scheduleProcessAggregateConfiguration.getSecondsToWaitForProcessStart());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.STD_ERR,
scheduleProcessAggregateConfiguration.getStdErr());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.STD_OUT,
scheduleProcessAggregateConfiguration.getStdOut());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.RETRY_ON_FAIL,
scheduleProcessAggregateConfiguration.isRetryOnFail());
}
/**
* Update the blackout router configuration.
*
* @param scheduledConsumerConfiguration
* @param scheduleProcessAggregateConfiguration
*/
private void updateBlackoutRouterConfiguration(ConfigurationMetaData> scheduledConsumerConfiguration
, ScheduledProcessAggregateConfiguration scheduleProcessAggregateConfiguration) {
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.CRON_EXPRESSIONS,
scheduleProcessAggregateConfiguration.getBlackoutCronExpressions());
this.setConfigurationParameterMetaDataValue(scheduledConsumerConfiguration, ScheduledProcessConfigurationConstants.DATE_TIME_RANGES,
scheduleProcessAggregateConfiguration.getBlackoutDateTimeRanges());
}
/**
* General method to set parameters on a configuration meta data.
*
* @param params
* @param paramName
* @param value
*/
private void setConfigurationParameterMetaDataValue(ConfigurationMetaData> params
, String paramName, Object value) {
params.getParameters().stream()
.filter(param -> param.getName().equals(paramName))
.findFirst()
.ifPresentOrElse(conf -> conf.setValue(value), () -> logger.warn(String.format("Failed to set configuration parameter[%s]" +
", value[%s], configuration[%s]", paramName, value, params)));
}
/**
* Helper method to get a specific component configuration from the module.
*
* @param moduleMetaData
* @param flow
* @param component
* @return
*/
private ConfigurationMetaData getConfigurationForAgentFlowComponent(Optional moduleMetaData, String flow, String component) {
AtomicReference configurationMetaData = new AtomicReference<>();
moduleMetaData.ifPresentOrElse(metaData -> {
metaData.getFlows().stream()
.filter(flowMetaData -> flowMetaData.getName().equals(flow))
.findFirst().ifPresentOrElse(flowMetaData -> {
flowMetaData.getFlowElements().stream()
.filter(flowElementMetaData -> flowElementMetaData.getComponentName().equals(component))
.findFirst().ifPresentOrElse(id -> configurationMetaData.set(configurationRestService
.getConfiguredResourceConfiguration(agent.getUrl(), agent.getName(), flow, component))
, () -> {
throw new RuntimeException(String.format("Could not load configuration metadata for agent[%s], flow[%s], component[%s] at url[%s]!"
, agent.getName(), flow, component, agent.getUrl()));
});
}, () -> {
throw new RuntimeException(String.format("Could not load flow for agent[%s], flow[%s], component[%s] at url[%s]!"
, agent.getName(), flow, component, agent.getUrl()));
});
}, () -> {
throw new RuntimeException(String.format("Could not load module metadata for agent[%s] at url[%s]!", agent.getName(), agent.getUrl()));
});
return configurationMetaData.get();
}
/**
* Helper method to set controls on the form elements if the form is read only
* or editable.
*
* @param enabled
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
this.timezoneCb.setEnabled(enabled);
this.jobNameTf.setEnabled(this.editMode == EditMode.NEW || this.editMode == EditMode.CLONE);
this.jobGroupTf.setEnabled(enabled);
this.jobDescriptionTa.setEnabled(enabled);
this.cronExpressionTf.setEnabled(enabled);
this.timezoneCb.setEnabled(enabled);
this.passThroughPropertiesButton.setVisible(enabled);
this.passThroughProperties.forEach(passThroughProperty -> {
passThroughProperty.nameTf.setEnabled(enabled);
passThroughProperty.valueTf.setEnabled(enabled);
});
if(this.passThroughProperties.size() == 0 && !enabled) {
this.noPassThoughPropertiesLabel.setVisible(true);
}
else {
this.noPassThoughPropertiesLabel.setVisible(false);
this.passThroughPropertiesDiv.setVisible(true);
}
this.commandLineTa.setEnabled(enabled);
this.workingDirectoryTf.setEnabled(enabled);
this.secondsToWaitForProcessStartTf.setEnabled(enabled);
this.stdOutTf.setEnabled(enabled);
this.stdErrTf.setEnabled(enabled);
this.retryOnFailCb.setEnabled(enabled);
this.successfulReturnCodes.forEach(successfulReturnCode -> successfulReturnCode.setEnabled(enabled));
this.successfulReturnCodesButton.setVisible(enabled);
if(this.successfulReturnCodes.size() == 0 && !enabled) {
this.noReturnCodesLabel.setVisible(true);
}
else {
this.noReturnCodesLabel.setVisible(false);
this.returnCodesDiv.setVisible(true);
}
this.addBlackoutCron.setVisible(enabled);
this.blackoutCronExpressions.forEach(blackoutCronExpression -> blackoutCronExpression.setEnabled(enabled));
if(this.blackoutCronExpressions.size() == 0 && !enabled) {
this.noBlackOutCronExpressionLabel.setVisible(true);
}
else {
this.noBlackOutCronExpressionLabel.setVisible(false);
}
this.addDateTimeRange.setVisible(enabled);
this.dateTimeRanges.forEach(blackoutCronExpression -> {
blackoutCronExpression.startDate.setEnabled(enabled);
blackoutCronExpression.startTime.setEnabled(enabled);
blackoutCronExpression.endDate.setEnabled(enabled);
blackoutCronExpression.endTime.setEnabled(enabled);
});
if(this.dateTimeRanges.size() == 0 && !enabled) {
this.noBlackOutDateTimeRangesLabel.setVisible(true);
}
else {
this.noBlackOutDateTimeRangesLabel.setVisible(false);
}
this.saveButton.setVisible(enabled);
this.cancelButton.setVisible(enabled);
}
/**
* Set the underlying pojo for the form along with the edit mode.
*
* @param scheduleProcessAggregateConfiguration
* @param editMode
*/
public void setScheduleProcessAggregateConfiguration(ScheduledProcessAggregateConfiguration scheduleProcessAggregateConfiguration, EditMode editMode) {
this.enabled = editMode == EditMode.NEW || editMode == EditMode.EDIT || editMode == EditMode.CLONE ? true : false;
if(editMode == EditMode.CLONE) {
scheduleProcessAggregateConfiguration.setJobName(null);
}
this.scheduleProcessAggregateConfiguration = scheduleProcessAggregateConfiguration;
this.oldScheduleProcessAggregateConfiguration = scheduleProcessAggregateConfiguration;
this.formBinder.readBean(this.scheduleProcessAggregateConfiguration);
this.timezoneCb.setValue(DateTimeUtil.getTimezonePairForZoneId(scheduleProcessAggregateConfiguration.getTimezone()));
this.bindCollections(scheduleProcessAggregateConfiguration);
this.editMode = editMode;
// make sure all value are bound before calling set enabled
this.setEnabled(this.enabled);
}
/**
* Bind all collection fields to the form
*
* @param scheduleProcessAggregateConfiguration
*/
private void bindCollections(ScheduledProcessAggregateConfiguration scheduleProcessAggregateConfiguration) {
scheduleProcessAggregateConfiguration.getPassthroughProperties().forEach((k,v) -> this.addPassThroughProperties(k,v));
scheduleProcessAggregateConfiguration.getSuccessfulReturnCodes().forEach(rc -> this.addSuccessfulReturnCodes(rc));
scheduleProcessAggregateConfiguration.getBlackoutCronExpressions().forEach(ce -> this.addBlackoutCronExpression(ce));
scheduleProcessAggregateConfiguration.getBlackoutDateTimeRanges().forEach((k,v) -> this.addDateTimeRange(Long.parseLong(k),
Long.parseLong(v)));
}
/**
* Helper method to add the controls for a blackout date time ranges
*
* @param startMilli
* @param endMilli
*/
private void addDateTimeRange(long startMilli, long endMilli) {
DateTimeRange dateTimeRange = new DateTimeRange();
dateTimeRange.startDate = new SuperDatePicker(getTranslation("label.start-date", UI.getCurrent().getLocale()));
dateTimeRange.startDate.setId("dateTimeRange.startDate"+this.dateTimeRanges.size());
dateTimeRange.startDate.setEnabled(this.enabled);
dateTimeRange.startDate.setDatePattern(DatePatterns.D_MMMM_YYYY);
dateTimeRange.startDate.setErrorMessage(getTranslation("error.missing-start-date", UI.getCurrent().getLocale()));
dateTimeRange.startDate.setLocale(UI.getCurrent().getLocale());
if(startMilli > 0) {
dateTimeRange.startDate.setValue(Instant.ofEpochMilli(startMilli).atZone(ZoneId.systemDefault()).toLocalDate());
}
dateTimeRange.startTime = new TimePicker(getTranslation("label.from", UI.getCurrent().getLocale()));
dateTimeRange.startTime.setId("dateTimeRange.startTime"+this.dateTimeRanges.size());
dateTimeRange.startTime.setEnabled(this.enabled);
dateTimeRange.startTime.setErrorMessage(getTranslation("error.missing-start-time", UI.getCurrent().getLocale()));
dateTimeRange.startTime.setLocale(UI.getCurrent().getLocale());
if(startMilli > 0) {
dateTimeRange.startTime.setValue(Instant.ofEpochMilli(startMilli).atZone(ZoneId.systemDefault()).toLocalTime());
}
HorizontalLayout startLayout = new HorizontalLayout();
startLayout.add(dateTimeRange.startDate, dateTimeRange.startTime);
dateTimeRange.endDate = new SuperDatePicker(getTranslation("label.end-date", UI.getCurrent().getLocale()));
dateTimeRange.endDate.setId("dateTimeRange.endDate"+this.dateTimeRanges.size());
dateTimeRange.endDate.setEnabled(this.enabled);
dateTimeRange.endDate.setDatePattern(DatePatterns.D_MMMM_YYYY);
dateTimeRange.endDate.setErrorMessage(getTranslation("error.missing-end-date", UI.getCurrent().getLocale()));
dateTimeRange.endDate.setLocale(UI.getCurrent().getLocale());
if(endMilli > 0) {
dateTimeRange.endDate.setValue(Instant.ofEpochMilli(endMilli).atZone(ZoneId.systemDefault()).toLocalDate());
}
dateTimeRange.endTime = new TimePicker(getTranslation("label.to", UI.getCurrent().getLocale()));
dateTimeRange.endTime.setId("dateTimeRange.endTime"+this.dateTimeRanges.size());
dateTimeRange.endTime.setEnabled(this.enabled);
dateTimeRange.endTime.setErrorMessage(getTranslation("error.missing-end-time", UI.getCurrent().getLocale()));
dateTimeRange.endTime.setLocale(UI.getCurrent().getLocale());
if(endMilli > 0) {
dateTimeRange.endTime.setValue(Instant.ofEpochMilli(endMilli).atZone(ZoneId.systemDefault()).toLocalTime());
}
HorizontalLayout endLayout = new HorizontalLayout();
this.dateTimeRanges.add(dateTimeRange);
Button minusButton = new Button(VaadinIcon.MINUS.create(), ev -> {
formLayout.remove(startLayout);
formLayout.remove(endLayout);
this.dateTimeRanges.remove(dateTimeRange);
});
minusButton.setVisible(this.enabled);
endLayout.add(dateTimeRange.endDate, dateTimeRange.endTime, minusButton);
formLayout.add(startLayout, endLayout);
if(!this.enabled){
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(this.noBlackOutDateTimeRangesLabel.getElement()) + 1, new Div());
}
}
/**
* Helper method to add the controls for pass through properties
*
* @param name
* @param value
*/
private void addPassThroughProperties(String name, String value) {
TextFieldNameValuePair nvp = new TextFieldNameValuePair();
nvp.nameTf = new TextField(getTranslation("label.property-name", UI.getCurrent().getLocale()));
nvp.nameTf.setId("passThroughProperty.nameTf"+this.passThroughProperties.size());
nvp.nameTf.setWidth("95%");
nvp.nameTf.setEnabled(this.enabled);
nvp.nameTf.setErrorMessage(getTranslation("error.missing-property-name", UI.getCurrent().getLocale()));
if(name!=null)nvp.nameTf.setValue(name);
HorizontalLayout startLayout = new HorizontalLayout();
startLayout.add(nvp.nameTf);
nvp.valueTf = new TextField(getTranslation("label.property-value", UI.getCurrent().getLocale()));
nvp.valueTf.setId("passThroughProperty.valueTf"+this.passThroughProperties.size());
nvp.valueTf.setEnabled(this.enabled);
nvp.valueTf.setWidth("95%");
nvp.valueTf.setErrorMessage(getTranslation("error.missing-property-value", UI.getCurrent().getLocale()));
if(value!=null)nvp.valueTf.setValue(value);
HorizontalLayout endLayout = new HorizontalLayout();
this.passThroughProperties.add(nvp);
Button minusButton = new Button(VaadinIcon.MINUS.create(), ev -> {
formLayout.remove(startLayout);
formLayout.remove(endLayout);
this.passThroughProperties.remove(nvp);
});
minusButton.setVisible(this.enabled);
endLayout.add(nvp.valueTf, minusButton);
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(passThroughPropertiesLabel.getElement()) + (!this.enabled ? 4 : 3), startLayout);
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(passThroughPropertiesLabel.getElement()) + (!this.enabled ? 5 : 4), endLayout);
}
/**
* Helper method to add the controls for the return codes
*
* @param returnCode
*/
private void addSuccessfulReturnCodes(String returnCode) {
TextField successfulReturnCodeTf = new TextField(getTranslation("label.successful-return-code", UI.getCurrent().getLocale()));
successfulReturnCodeTf.setId("successfulReturnCodeTf"+this.successfulReturnCodes.size());
successfulReturnCodeTf.setEnabled(this.enabled);
this.successfulReturnCodes.add(successfulReturnCodeTf);
successfulReturnCodeTf.setErrorMessage(getTranslation("error.missing-return-code", UI.getCurrent().getLocale()) );
if(returnCode!=null)successfulReturnCodeTf.setValue(returnCode);
Button minusButton = new Button(VaadinIcon.MINUS.create(), ev -> {
formLayout.remove(successfulReturnCodeTf);
formLayout.remove(ev.getSource());
this.successfulReturnCodes.remove(successfulReturnCodeTf);
});
minusButton.setVisible(this.enabled);
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(successfulReturnCodesLabel.getElement()) + (!this.enabled ? 4 : 3), successfulReturnCodeTf);
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(successfulReturnCodesLabel.getElement()) + (!this.enabled ? 5 : 4), minusButton);
}
/**
* Helper method to add the controls for a blackout cron expression
*
* @param cron
*/
private void addBlackoutCronExpression(String cron) {
TextField blackoutCronExpressionTf = new TextField(getTranslation("label.blackout-cron-expression", UI.getCurrent().getLocale()));
blackoutCronExpressionTf.setId("blackoutCronExpressionTf"+this.blackoutCronExpressions.size());
blackoutCronExpressionTf.setEnabled(this.enabled);
this.blackoutCronExpressions.add(blackoutCronExpressionTf);
blackoutCronExpressionTf.setErrorMessage(getTranslation("error.missing-blackout-cron-expression", UI.getCurrent().getLocale()));
if(cron!=null)blackoutCronExpressionTf.setValue(cron);
Button minusButton = new Button(VaadinIcon.MINUS.create(), ev -> {
formLayout.remove(blackoutCronExpressionTf);
formLayout.remove(ev.getSource());
this.blackoutCronExpressions.remove(blackoutCronExpressionTf);
});
minusButton.setVisible(this.enabled);
this.blackOutCronExpressionDiv.setVisible(!this.enabled);
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(blackOutCronExpressionLabel.getElement()) + (!this.enabled ? 2 : 3), blackoutCronExpressionTf);
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(blackOutCronExpressionLabel.getElement()) + (!this.enabled ? 5 : 4), minusButton);
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(blackOutCronExpressionLabel.getElement()) + (!this.enabled ? 1 : 4), blackOutCronExpressionDiv);
if(!this.enabled){
formLayout.addComponentAtIndex(formLayout.getElement().indexOfChild(blackoutCronExpressionTf.getElement()) + 1, new Div());
}
}
/** Private helper classes */
private class TextFieldNameValuePair {
public TextField nameTf;
public TextField valueTf;
}
private class DateTimeRange {
public SuperDatePicker startDate;
public TimePicker startTime;
public SuperDatePicker endDate;
public TimePicker endTime;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy