Java tutorial
/** * Copyright (c) Siemens AG, 2018 * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.hawkbit.ui.management.footer; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper; import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import com.cronutils.descriptor.CronDescriptor; import com.vaadin.data.Validator; import com.vaadin.event.FieldEvents.TextChangeEvent; import com.vaadin.event.FieldEvents.TextChangeListener; import com.vaadin.server.Page; import com.vaadin.ui.ComboBox; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ValoTheme; /** * {@link MaintenanceWindowLayout} defines UI layout that is used to specify the * maintenance schedule while assigning distribution set(s) to the target(s). */ public class MaintenanceWindowLayout extends VerticalLayout { private static final long serialVersionUID = 1L; private final VaadinMessageSource i18n; private static final String CRON_VALIDATION_ERROR = "message.maintenancewindow.schedule.validation.error"; private TextField schedule; private TextField duration; private ComboBox timeZone; private Label scheduleTranslator; /** * Constructor for the control to specify the maintenance schedule. * * @param i18n * (@link VaadinMessageSource} to get the localized resource * strings. */ public MaintenanceWindowLayout(final VaadinMessageSource i18n) { this.i18n = i18n; createMaintenanceScheduleControl(); createMaintenanceDurationControl(); createMaintenanceTimeZoneControl(); createMaintenanceScheduleTranslatorControl(); final HorizontalLayout controlContainer = new HorizontalLayout(); controlContainer.addComponent(schedule); controlContainer.addComponent(duration); controlContainer.addComponent(timeZone); addComponent(controlContainer); addComponent(scheduleTranslator); setStyleName("dist-window-maintenance-window-layout"); setId(UIComponentIdProvider.MAINTENANCE_WINDOW_LAYOUT_ID); } /** * Text field to specify the schedule. */ private void createMaintenanceScheduleControl() { schedule = new TextFieldBuilder(Action.MAINTENANCE_WINDOW_SCHEDULE_LENGTH) .id(UIComponentIdProvider.MAINTENANCE_WINDOW_SCHEDULE_ID) .caption(i18n.getMessage("caption.maintenancewindow.schedule")).validator(new CronValidator()) .prompt("0 0 3 ? * 6").required(true, i18n).buildTextComponent(); schedule.addTextChangeListener(new CronTranslationListener()); } /** * Validates if the maintenance schedule is a valid cron expression. */ private class CronValidator implements Validator { private static final long serialVersionUID = 1L; // Exception squid:S1166 - Vaadin validation class, // InvalidValueException, // doesn't have the constructor to pass throwable, but shows the // validation // errors to the user @SuppressWarnings("squid:S1166") @Override public void validate(final Object value) { try { MaintenanceScheduleHelper.validateCronSchedule((String) value); } catch (final InvalidMaintenanceScheduleException e) { throw new InvalidValueException(i18n.getMessage(CRON_VALIDATION_ERROR) + ": " + e.getMessage()); } } } /** * Used for cron expression translation. */ private class CronTranslationListener implements TextChangeListener { private static final long serialVersionUID = 1L; private final transient CronDescriptor cronDescriptor; public CronTranslationListener() { cronDescriptor = CronDescriptor.instance(getClientsLocale()); } @Override public void textChange(final TextChangeEvent event) { scheduleTranslator.setValue(translateCron(event.getText())); } // Exception squid:S1166 - when the format of the cron expression is not // valid, the hint is shown to provide the valid one @SuppressWarnings("squid:S1166") private String translateCron(final String cronExpression) { try { return cronDescriptor.describe(MaintenanceScheduleHelper.getCronFromExpression(cronExpression)); } catch (final IllegalArgumentException ex) { return i18n.getMessage(CRON_VALIDATION_ERROR); } } private Locale getClientsLocale() { return Page.getCurrent().getWebBrowser().getLocale(); } } /** * Text field to specify the duration. */ private void createMaintenanceDurationControl() { duration = new TextFieldBuilder(Action.MAINTENANCE_WINDOW_DURATION_LENGTH) .id(UIComponentIdProvider.MAINTENANCE_WINDOW_DURATION_ID) .caption(i18n.getMessage("caption.maintenancewindow.duration")).validator(new DurationValidator()) .prompt("hh:mm:ss").required(true, i18n).buildTextComponent(); } /** * Validates if the duration is specified in expected format. */ private class DurationValidator implements Validator { private static final long serialVersionUID = 1L; // Exception squid:S1166 - Vaadin validation class, // InvalidValueException, // doesn't have the constructor to pass throwable, but shows the // validation // errors to the user @SuppressWarnings("squid:S1166") @Override public void validate(final Object value) { try { MaintenanceScheduleHelper.validateDuration((String) value); } catch (final InvalidMaintenanceScheduleException e) { throw new InvalidValueException(i18n.getMessage( "message.maintenancewindow.duration.validation.error", e.getDurationErrorIndex())); } } } /** * Combo box to pick the time zone offset. */ private void createMaintenanceTimeZoneControl() { // ComboBoxBuilder cannot be used here, because Builder do // 'comboBox.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);' // which interferes our code: 'timeZone.addItems(getAllTimeZones());' timeZone = new ComboBox(); timeZone.setId(UIComponentIdProvider.MAINTENANCE_WINDOW_TIME_ZONE_ID); timeZone.setCaption(i18n.getMessage("caption.maintenancewindow.timezone")); timeZone.addItems(getAllTimeZones()); timeZone.setValue(getClientTimeZone()); timeZone.addStyleName(ValoTheme.COMBOBOX_SMALL); timeZone.setTextInputAllowed(false); timeZone.setNullSelectionAllowed(false); } /** * Get list of all time zone offsets supported. */ private static List<String> getAllTimeZones() { final List<String> lst = ZoneId.getAvailableZoneIds().stream() .map(id -> ZonedDateTime.now(ZoneId.of(id)).getOffset().getId().replace("Z", "+00:00")).distinct() .collect(Collectors.toList()); lst.sort(null); return lst; } /** * Get time zone of the browser client to be used as default. */ private static String getClientTimeZone() { return ZonedDateTime.now(SPDateTimeUtil.getTimeZoneId(SPDateTimeUtil.getBrowserTimeZone())).getOffset() .getId().replaceAll("Z", "+00:00"); } /** * Label to translate the cron schedule to human readable format. */ private void createMaintenanceScheduleTranslatorControl() { scheduleTranslator = new LabelBuilder().id(UIComponentIdProvider.MAINTENANCE_WINDOW_SCHEDULE_TRANSLATOR_ID) .name(i18n.getMessage(CRON_VALIDATION_ERROR)).buildLabel(); scheduleTranslator.addStyleName(ValoTheme.LABEL_TINY); } /** * Get the cron expression for maintenance schedule. * * @return {@link String}. */ public String getMaintenanceSchedule() { return schedule.getValue(); } /** * Get the maintenance window duration. * * @return {@link String}. */ public String getMaintenanceDuration() { return duration.getValue(); } /** * Get the timezone for maintenance window. * * @return {@link String}. */ public String getMaintenanceTimeZone() { return timeZone.getValue().toString(); } /** * Set all the controls to their default values. */ public void clearAllControls() { schedule.setValue(""); duration.setValue(""); timeZone.setValue(getClientTimeZone()); scheduleTranslator.setValue(i18n.getMessage(CRON_VALIDATION_ERROR)); } /** * Method, used for validity check, when schedule text is changed. * * @param event * (@link TextChangeEvent} the event object after schedule text * change. * @return validity of maintenance window controls. */ public boolean onScheduleChange(final TextChangeEvent event) { schedule.setValue(event.getText()); return isScheduleAndDurationValid(); } /** * Method, used for validity check, when duration text is changed. * * @param event * (@link TextChangeEvent} the event object after duration text * change. * @return validity of maintenance window controls. */ public boolean onDurationChange(final TextChangeEvent event) { duration.setValue(event.getText()); return isScheduleAndDurationValid(); } private boolean isScheduleAndDurationValid() { if (schedule.isEmpty() || duration.isEmpty()) { return false; } return schedule.isValid() && duration.isValid(); } public TextField getScheduleControl() { return schedule; } public TextField getDurationControl() { return duration; } }