diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java index d491319fa..f00b4880e 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java @@ -110,7 +110,7 @@ public class TargetInfo implements Persistable, Serializable { @CollectionTable(name = "sp_target_attributes", joinColumns = { @JoinColumn(name = "target_id") }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target") ) // use deprecated annotation until HHH-8862 is fixed - @SuppressWarnings("deprecation") + // @org.hibernate.annotations.ForeignKey( name = "fk_targ_attrib_target" ) private final Map controllerAttributes = Collections.synchronizedMap(new HashMap()); @@ -181,7 +181,7 @@ public class TargetInfo implements Persistable, Serializable { } /** - * @param ipAddress + * @param address * the ipAddress to set * * @throws IllegalArgumentException @@ -382,6 +382,9 @@ public class TargetInfo implements Persistable, Serializable { return overdueDate; } + /** + * @return the current date + */ public LocalDateTime getCurrentDate() { return currentDate; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/ConfigurationElement.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/ConfigurationElement.java new file mode 100644 index 000000000..7614f852f --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/ConfigurationElement.java @@ -0,0 +1,35 @@ +package org.eclipse.hawkbit.ui.tenantconfiguration; + +import java.io.Serializable; + +public interface ConfigurationElement { + + /** + * called to verify that the Input done by the user is valid + * + * @return true when the data is valid, false otherwise + */ + boolean isUserInputValid(); + + /** + * Adds a configuration change listener to notify about configuration + * changes. + * + * @param listener + * the listener to be notified in case the item changes some + * configuration + */ + void addChangeListener(ConfigurationGroupChangeListener listener); + + /** + * Configuration Change Listener to be notified about configuration changes + * in configuration group. + * + */ + interface ConfigurationGroupChangeListener extends Serializable { + /** + * called to notify about configuration has been changed. + */ + void configurationChanged(); + } +} \ No newline at end of file diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/ConfigurationGroup.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/ConfigurationGroup.java index 60f1934f9..1d2245394 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/ConfigurationGroup.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/ConfigurationGroup.java @@ -8,8 +8,6 @@ */ package org.eclipse.hawkbit.ui.tenantconfiguration; -import java.io.Serializable; - import com.vaadin.ui.Component; /** @@ -17,7 +15,7 @@ import com.vaadin.ui.Component; * * */ -public interface ConfigurationGroup extends Component { +public interface ConfigurationGroup extends Component, ConfigurationElement { /** * called to store any configuration changes. @@ -28,34 +26,4 @@ public interface ConfigurationGroup extends Component { * called to rollback any configuration changes. */ void undo(); - - /** - * called to verify that the Input done by the user is valid - * - * @return true when the data is valid, false otherwise - */ - boolean isUserInputValid(); - - /** - * Adds a configuration change listener to notify about configuration - * changes. - * - * @param listener - * the listener to be notified in case the item changes some - * configuration - */ - void addChangeListener(ConfigurationGroupChangeListener listener); - - /** - * Configuration Change Listener to be notified about configuration changes - * in configuration group. - * - */ - interface ConfigurationGroupChangeListener extends Serializable { - /** - * called to notify about configuration has been changed. - */ - void configurationChanged(); - } - } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/PollingConfigurationView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/PollingConfigurationView.java index 3a5fb92a0..b57c960a2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/PollingConfigurationView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/PollingConfigurationView.java @@ -1,23 +1,18 @@ package org.eclipse.hawkbit.ui.tenantconfiguration; -import static org.eclipse.hawkbit.repository.model.helper.PollConfigurationHelper.EXPECTED_POLLING_TIME_FORMAT; +import java.time.Duration; import javax.annotation.PostConstruct; -import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.model.helper.PollConfigurationHelper; -import org.eclipse.hawkbit.ui.tenantconfiguration.polling.DurationField; +import org.eclipse.hawkbit.ui.tenantconfiguration.polling.DurationConfigField; import org.eclipse.hawkbit.ui.utils.I18N; import org.springframework.beans.factory.annotation.Autowired; -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Validator; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; -import com.vaadin.ui.Field; import com.vaadin.ui.Label; import com.vaadin.ui.Panel; -import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; /** @@ -29,21 +24,24 @@ import com.vaadin.ui.VerticalLayout; @SpringComponent @ViewScope public class PollingConfigurationView extends BaseConfigurationView - implements ConfigurationGroup, Field.ValueChangeListener { + implements ConfigurationGroup, ConfigurationElement.ConfigurationGroupChangeListener { private static final long serialVersionUID = 1L; - @Autowired - private transient SystemManagement systemManagement; - @Autowired private I18N i18n; @Autowired PollConfigurationHelper pollConfigurationHelper; - final private DurationField fieldPollingTime = new DurationField(); - final private DurationField fieldPollingOverdueTime = new DurationField(); + @Autowired + private DurationConfigField fieldPollingTime; + + @Autowired + private DurationConfigField fieldPollingOverdueTime; + + private Duration tenantPollingTime; + private Duration tenantPollingOverdueTime; /** * Initialize Authentication Configuration layout. @@ -51,38 +49,34 @@ public class PollingConfigurationView extends BaseConfigurationView @PostConstruct public void init() { - Validator correctFormatValidator = new Validator() { - private static final long serialVersionUID = 1L; - - @Override - public void validate(Object value) throws InvalidValueException { - if (!(value instanceof String) || !((String) value).matches(EXPECTED_POLLING_TIME_FORMAT)) { - throw new InvalidValueException("Not in HH:MM:SS Format."); - } - } - }; - final Panel rootPanel = new Panel(); rootPanel.setSizeFull(); - rootPanel.addStyleName("config-panel"); - // TODO Better Layout than Vertical Layout - maybe a table layout? final VerticalLayout vLayout = new VerticalLayout(); vLayout.setMargin(true); - vLayout.setSizeFull(); + // vLayout.setSizeFull(); final Label headerDisSetType = new Label(i18n.get("configuration.polling.title")); headerDisSetType.addStyleName("config-panel-header"); vLayout.addComponent(headerDisSetType); - final Label labelPollingTime = new Label(i18n.get("configuration.polling.time")); - vLayout.addComponent(labelPollingTime); + tenantPollingTime = pollConfigurationHelper.getTenantPollTimeIntervall(); + fieldPollingTime.setInitValues(i18n.get("configuration.polling.time"), tenantPollingTime, + pollConfigurationHelper.getGlobalPollTimeInterval()); + fieldPollingTime.setAllowedRange(pollConfigurationHelper.getMinimumPollingInterval(), + pollConfigurationHelper.getMaximumPollingInterval()); + fieldPollingTime.addChangeListener(this); vLayout.addComponent(fieldPollingTime); - final Label labelPollingOverdueTime = new Label(i18n.get("configuration.polling.overduetime")); - vLayout.addComponent(labelPollingOverdueTime); + tenantPollingOverdueTime = pollConfigurationHelper.getTenantOverduePollTimeIntervall(); + + fieldPollingOverdueTime.setInitValues(i18n.get("configuration.polling.overduetime"), tenantPollingOverdueTime, + pollConfigurationHelper.getGlobalOverduePollTimeInterval()); + fieldPollingOverdueTime.setAllowedRange(pollConfigurationHelper.getMinimumPollingInterval(), + pollConfigurationHelper.getMaximumPollingInterval()); + fieldPollingOverdueTime.addChangeListener(this); vLayout.addComponent(fieldPollingOverdueTime); @@ -90,55 +84,47 @@ public class PollingConfigurationView extends BaseConfigurationView setCompositionRoot(rootPanel); } - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.Property.ValueChangeListener#valueChange(com.vaadin.data. - * Property.ValueChangeEvent) - * - * This method is called when a value of a textField changes. When the value - * is not in the correct format, but has valid data, this method will change - * the value to the correct format - */ @Override - public void valueChange(ValueChangeEvent event) { + public void save() { + // make sure values are only saved, when the value has been changed - notifyConfigurationChanged(); + if (!compareDurations(tenantPollingTime, fieldPollingTime.getValue())) { + tenantPollingTime = fieldPollingTime.getValue(); + pollConfigurationHelper.setTenantPollTimeIntervall(fieldPollingTime.getValue()); + } - if (event.getProperty() instanceof TextField) { - TextField textfield = (TextField) event.getProperty(); - - String value = textfield.getValue(); - - if (value.matches("[0-9]{1,6}")) { - value = "000000".substring(value.length()) + value; - value = value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6); - } - - if (value.matches("([0-5]?[0-9]?(:[0-5][0-9]){1,2})")) { - value = "00:00:00".substring(0, 8 - value.length()) + value; - } - - if (value.matches(EXPECTED_POLLING_TIME_FORMAT)) { - textfield.setValue(value); - } + if (!compareDurations(tenantPollingOverdueTime, fieldPollingOverdueTime.getValue())) { + tenantPollingOverdueTime = fieldPollingOverdueTime.getValue(); + pollConfigurationHelper.setTenantOverduePollTimeIntervall(fieldPollingOverdueTime.getValue()); } } - @Override - public void save() { - // TODO Auto-generated method stub - } - @Override public void undo() { - + fieldPollingTime.setValue(tenantPollingTime); + fieldPollingOverdueTime.setValue(tenantPollingOverdueTime); } @Override public boolean isUserInputValid() { - return fieldPollingTime.isValid() && fieldPollingOverdueTime.isValid(); + return fieldPollingTime.isUserInputValid() && fieldPollingOverdueTime.isUserInputValid(); } + @Override + public void configurationChanged() { + notifyConfigurationChanged(); + } + + private boolean compareDurations(Duration d1, Duration d2) { + if (d1 == null && d2 == null) { + return true; + } + + if (d1 != null) { + return d1.equals(d2); + } + + // d1 == null, d2 != null + return false; + } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/TenantConfigurationDashboardView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/TenantConfigurationDashboardView.java index 94e7d9fb7..b8873a5bb 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/TenantConfigurationDashboardView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/TenantConfigurationDashboardView.java @@ -17,7 +17,7 @@ import org.eclipse.hawkbit.ui.HawkbitUI; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; import org.eclipse.hawkbit.ui.documentation.DocumentationPageLink; -import org.eclipse.hawkbit.ui.tenantconfiguration.ConfigurationGroup.ConfigurationGroupChangeListener; +import org.eclipse.hawkbit.ui.tenantconfiguration.ConfigurationElement.ConfigurationGroupChangeListener; import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.SPUIComponetIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/polling/DurationConfigField.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/polling/DurationConfigField.java new file mode 100644 index 000000000..5555037a6 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/polling/DurationConfigField.java @@ -0,0 +1,181 @@ +package org.eclipse.hawkbit.ui.tenantconfiguration.polling; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.validation.constraints.NotNull; + +import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; +import org.eclipse.hawkbit.ui.tenantconfiguration.ConfigurationElement; +import org.eclipse.hawkbit.ui.utils.I18N; +import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.spring.annotation.SpringComponent; +import com.vaadin.spring.annotation.ViewScope; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.Label; + +/** + * The DurationConfigField consists of three vaadin fields. A {@link #Label} + * {@link #DurationField} and a {@link #CheckBox}. The user can then enter a + * duration in the DurationField or he can configure using the global duration + * by changing the CheckBox. + */ +@SpringComponent +@ViewScope +@Scope("prototype") +public class DurationConfigField extends GridLayout implements ValueChangeListener, ConfigurationElement { + + private static final long serialVersionUID = 1L; + + private final List configurationChangeListeners = new ArrayList<>(); + + private CheckBox checkBox; + private DurationField durationField; + + private Duration globalDuration; + + @Autowired + private I18N i18n; + + /** + * sets i18n + * + * @param i18n + */ + public void setI18n(I18N i18n) { + this.i18n = i18n; + } + + public DurationConfigField() { + super(3, 2); + } + + /** + * Initialize Authentication Configuration layout. + */ + @PostConstruct + public void init() { + + this.addStyleName("duration-config-field"); + this.setSpacing(true); + this.setImmediate(true); + this.setColumnExpandRatio(1, 1.0F); + // gridLayout.setSizeFull(); + + checkBox = new CheckBox(); + + this.addComponent(checkBox, 0, 0); + this.setComponentAlignment(checkBox, Alignment.MIDDLE_LEFT); + + Label customValue = SPUIComponentProvider.getLabel(i18n.get("configuration.polling.custom.value"), + SPUILabelDefinitions.SP_LABEL_SIMPLE); + this.addComponent(customValue, 1, 0); + this.setComponentAlignment(customValue, Alignment.MIDDLE_LEFT); + + durationField = new DurationField(); + + this.addComponent(durationField, 2, 0); + this.setComponentAlignment(durationField, Alignment.MIDDLE_LEFT); + + checkBox.addValueChangeListener(this); + } + + @Override + public void valueChange(ValueChangeEvent event) { + if (event.getProperty() == checkBox) { + if (checkBox.getValue()) { + durationField.setEnabled(true); + } else { + durationField.setDuration(globalDuration); + durationField.setEnabled(false); + } + } + notifyConfigurationChanged(); + } + + /** + * sets all mandatitory attributes for correct user interaction + * + * @param caption + * the caption of the field + * + * @param tenantDuration + * tenant specific duration value + * @param globalDuration + * duration value which is stored in the global configuration + */ + public void setInitValues(String caption, @NotNull Duration tenantDuration, @NotNull Duration globalDuration) { + this.setCaption(caption); + this.globalDuration = globalDuration; + + this.setValue(tenantDuration); + } + + /** + * sets the allowed range of the duration values + * + * @param minimumDuration + * minimum allowed duration value + * @param maximumDuration + * maximum allowed duration value + */ + public void setAllowedRange(Duration minimumDuration, Duration maximumDuration) { + durationField.setMinimumDuration(minimumDuration); + durationField.setMaximumDuration(maximumDuration); + + } + + /** + * Set the value of the duration field + * + * @param tenantDuration + * duration which will be set in to the duration field, when + * {@code null} the global configuration will be used. + */ + public void setValue(Duration tenantDuration) { + if (tenantDuration == null) { + // no tenant specific configuration + checkBox.setValue(false); + durationField.setDuration(globalDuration); + durationField.setEnabled(false); + } else { + checkBox.setValue(true); + durationField.setDuration(tenantDuration); + durationField.setEnabled(true); + } + } + + /** + * @return the duration of the duration field or null, when the user has + * configured to use the global value. + */ + public Duration getValue() { + if (checkBox.getValue()) { + return durationField.getDuration(); + } + return null; + } + + @Override + public boolean isUserInputValid() { + return !checkBox.getValue() || (durationField.isValid() && durationField.getValue() != null); + } + + private void notifyConfigurationChanged() { + configurationChangeListeners.forEach(listener -> listener.configurationChanged()); + } + + @Override + public void addChangeListener(final ConfigurationGroupChangeListener listener) { + configurationChangeListeners.add(listener); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/polling/DurationField.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/polling/DurationField.java index 7417ddc1b..f5b33354e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/polling/DurationField.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/polling/DurationField.java @@ -2,17 +2,27 @@ package org.eclipse.hawkbit.ui.tenantconfiguration.polling; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.Date; import java.util.Locale; +import java.util.TimeZone; + +import javax.validation.constraints.NotNull; import com.vaadin.data.Property; +import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.converter.Converter.ConversionException; import com.vaadin.shared.ui.datefield.Resolution; import com.vaadin.ui.DateField; +import com.vaadin.ui.themes.ValoTheme; /** * This class represents a Field which is optimized to enter a time duration in - * form HH:mm:ss (see {@link #DEFAULT_DURATION_FORMAT}). It uses the vaadin + * form HH:mm:ss (see {@link #DURATION_FORMAT_STIRNG}). It uses the vaadin * DateField as a basic element, but the format is optimized for the duration * input. For a correct view of the popup it is recommended not to display the * css-class "v-datefield-calendarpanel-header" and @@ -22,42 +32,61 @@ public class DurationField extends DateField { private static final long serialVersionUID = 1L; - private static String CSS_STYLE_NAME = "durationfield"; + private static final String CSS_STYLE_NAME = "durationfield"; - private static String DEFAULT_DURATION_FORMAT = "HH:mm:ss"; - private static String ADDITIONAL_DURATION_FORMAT = "HHmmss"; + private static final String ADDITIONAL_DURATION_STRING = "HHmmss"; + private static final String DURATION_FORMAT_STIRNG = "HH:mm:ss"; - private SimpleDateFormat default_format = new SimpleDateFormat(DEFAULT_DURATION_FORMAT); - private SimpleDateFormat additional_format = new SimpleDateFormat(ADDITIONAL_DURATION_FORMAT); + private static final ZoneId ZONEID_UTC = ZoneId.of("+0"); + + private static final Duration MAXIMUM_DURATION = Duration.ofHours(23).plusMinutes(59).plusSeconds(59); + + private final SimpleDateFormat durationFormat = new SimpleDateFormat(DURATION_FORMAT_STIRNG); + private final SimpleDateFormat additionalFormat = new SimpleDateFormat(ADDITIONAL_DURATION_STRING); + + private Date minimumDuration; + private Date maximumDuration; /** * Creates a DurationField */ - public DurationField() { + protected DurationField() { + super(); - default_format.setLenient(false); - additional_format.setLenient(false); + this.setTimeZone(TimeZone.getTimeZone(ZONEID_UTC)); + durationFormat.setTimeZone(TimeZone.getTimeZone(ZONEID_UTC)); + additionalFormat.setTimeZone(TimeZone.getTimeZone(ZONEID_UTC)); + durationFormat.setLenient(false); + additionalFormat.setLenient(false); this.setResolution(Resolution.SECOND); - this.setDateFormat(DEFAULT_DURATION_FORMAT); + this.setDateFormat(DURATION_FORMAT_STIRNG); this.addStyleName(CSS_STYLE_NAME); + this.addStyleName(ValoTheme.TEXTFIELD_TINY); + this.setWidth("100px"); // needed that popup shows a 24h clock this.setLocale(Locale.GERMANY); - // adds empty change Listener, but is needed that field reacts on - // pressed enter + this.addValueChangeListener(this); } + /** + * This method is called to handle a non-empty date string from the client + * if the client could not parse it as a Date. In the current case two + * different parsing schemas are tried. If parsing is not possible a + * ConversionException is thrown which marks the DurationField as invalid. + */ @Override protected Date handleUnparsableDateString(String value) throws ConversionException { try { - return default_format.parse(value); + return durationFormat.parse(value); } catch (ParseException e1) { try { - return additional_format.parse(value); + + return additionalFormat.parse("000000".substring(value.length() <= 6 ? value.length() : 6) + value); } catch (ParseException e2) { // if Parsing is not possible ConversionException is thrown } @@ -65,32 +94,128 @@ public class DurationField extends DateField { throw new ConversionException("input is not in HH:MM:SS format."); } - /** - * Sets the duration value as a String - * - * @param duration - * duration as String in format HH:mm:ss, only values <= 23:59:59 - * are excepted - * @throws ParseException - * Exception is thrown, when String parameter is in wrong - * format. - */ - public void setValueAsString(String duration) throws ParseException { - super.setValue(default_format.parse(duration)); - } + @Override + public void valueChange(Property.ValueChangeEvent event) { + // do not delete this method, even when removing the code inside this + // method. This method overwrites the super method, which is + // necessary, that parsing works correctly on pressing enter key - /** - * Gets the duration value as a formated String - * - * @return duration as String in format HH:mm:ss - */ - public String getValueAsString() { - return default_format.format(super.getValue()); + if (event.getProperty() instanceof DurationField) { + Date value = (Date) event.getProperty().getValue(); + + // setValue() calls valueChanged again, when the minimum is greater + // than the maximum this can lead to an endless loop + if (value != null && minimumDuration != null && maximumDuration != null + && minimumDuration.before(maximumDuration)) { + + if (compareTimeOfDates(value, maximumDuration) > 0) { + ((DateField) event.getProperty()).setValue(maximumDuration); + } + + if (compareTimeOfDates(minimumDuration, value) > 0) { + ((DateField) event.getProperty()).setValue(minimumDuration); + } + } + } } @Override - public void valueChange(Property.ValueChangeEvent event) { - // does nothing, but method overrides super methods and is needed that - // parsing works correctly on pressed enter key + public void validate(Date value) throws InvalidValueException { + super.validate(value); + + if (value != null && maximumDuration != null && compareTimeOfDates(value, maximumDuration) > 0) { + throw new InvalidValueException("value is greater than the allowed maximum value"); + } + + if (value != null && minimumDuration != null && compareTimeOfDates(minimumDuration, value) > 0) { + throw new InvalidValueException("value is smaller than the allowed minimum value"); + } + } + + /** + * Sets the duration value + * + * @param duration + * duration, only values <= 23:59:59 are excepted + */ + public void setDuration(@NotNull Duration duration) { + if (duration.compareTo(MAXIMUM_DURATION) > 0) { + throw new IllegalArgumentException("The duaration has to be smaller than 23:59:59."); + } + super.setValue(durationToDate(duration)); + } + + /** + * Gets the duration value of the TextField + * + * @return duration which is written in the vaadin Field + */ + public Duration getDuration() { + if (this.getValue() == null) { + return null; + } + return dateToDuration(this.getValue()); + } + + /** + * Sets the minimal allowed duration value as a String + * + * @param minimumDuration + * minimum Duration, only values smaller 23:59:59 are excepted + */ + public void setMinimumDuration(@NotNull Duration minimumDuration) { + if (minimumDuration.compareTo(MAXIMUM_DURATION) > 0) { + throw new IllegalArgumentException("The minimum duaration has to be smaller than 23:59:59."); + } + this.minimumDuration = durationToDate(minimumDuration); + } + + /** + * Sets the maximum allowed duration value as a String + * + * @param maximumDuration + * maximumDuration, only values smaller 23:59:59 are excepted + */ + public void setMaximumDuration(@NotNull Duration maximumDuration) { + if (maximumDuration.compareTo(MAXIMUM_DURATION) > 0) { + throw new IllegalArgumentException("The maximum duaration has to be smaller than 23:59:59."); + } + this.maximumDuration = durationToDate(maximumDuration); + } + + private static Date durationToDate(final Duration duration) { + if (duration.compareTo(MAXIMUM_DURATION) > 0) { + throw new IllegalArgumentException("The duaration has to be smaller than 23:59:59."); + } + + final LocalTime lt = LocalTime.ofNanoOfDay(duration.toNanos()); + return Date.from(lt.atDate(LocalDate.now(ZONEID_UTC)).atZone(ZONEID_UTC).toInstant()); + } + + private static Duration dateToDuration(final Date date) { + final LocalTime endExclusive = LocalDateTime.ofInstant(date.toInstant(), ZONEID_UTC).toLocalTime(); + return Duration.between(LocalTime.MIDNIGHT, LocalTime.from(endExclusive)); + } + + /** + * Because parsing done by base class returns a different date than parsing + * done by the user or converting duration to a date. But for the + * DurationField comparison only the time is important. This function helps + * comparing the time and ignores the values for day, month and year. + * + * @param d1 + * date, which time will compared with the time of d2 + * @param d2 + * date, which time will compared with the time of d1 + * @return the value 0 if the time represented d1 is equal to the time + * represented by d2; a value less than 0 if the time of d1 is + * before the time of d2; and a value greater than 0 if the time of + * d1 is after the time represented by d2. + */ + private int compareTimeOfDates(Date d1, Date d2) { + LocalTime lt1 = LocalDateTime.ofInstant(d1.toInstant(), ZONEID_UTC).toLocalTime(); + LocalTime lt2 = LocalDateTime.ofInstant(d2.toInstant(), ZONEID_UTC).toLocalTime(); + + return lt1.compareTo(lt2); } } diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 209569ff0..27103a0b9 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -400,6 +400,7 @@ configuration.authentication.title=Authentication Configuration configuration.polling.title=Polling Configuration configuration.polling.time=Polling Time configuration.polling.overduetime=Polling Overdue Time +configuration.polling.custom.value=use a custom value #Calendar calendar.year=year