package rocks.inspectit.ui.rcp.ci.wizard.page; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.math.NumberUtils; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Spinner; import org.eclipse.swt.widgets.Text; import rocks.inspectit.shared.all.util.EMailUtils; import rocks.inspectit.shared.all.util.Pair; import rocks.inspectit.shared.cs.ci.AlertingDefinition.ThresholdType; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.InspectITImages; import rocks.inspectit.ui.rcp.util.LineNumbersStyleListener; /** * Wizard Page for the definition of the alerting details. * * @author Alexander Wert * */ public class AlertDetailsWizardPage extends WizardPage { /** * New line characters. */ private static final String NEW_LINE = System.getProperty("line.separator"); /** * Description text for the email addresses input field. */ private static final String EMAIL_INFO_TEXT = "Specify email addresses to which notifications about an alert shall be sent.\n" + "Enter one address per line in the text box."; /** * Description text for the time range input field. */ private static final String TIMERANGE_INFO_TEXT = "Specify the interval in minutes for checking this rule.\n\n" + "Note:\nIf this rule applies to a duration metric\n" + "(e.g. business transaction or method response times),\n" + "the interval value should be of magnitutes higher (10x to 100x)\n" + "than the typical response times of your metric!\n" + "Otherwise, the alerting engine may miss violations that take too long."; /** * Description text for the threshold input fields. */ private static final String THRESHOLD_INFO_TEXT = "Specify the alerting threshold.\n" + "The unit of the threshold is determined by the previous\n" + "selection of the metric (measurement + field).\n" + "Specify with the checkbox whether the given threshold is a lower or upper threshold."; /** * Title of the wizard page. */ private static final String TITLE = "Alert Threshold"; /** * Default message of the wizard page. */ private static final String DEFAULT_MESSAGE = "Define the threshold and check interval for the new alert definition."; /** * Default value for the time range / check interval. */ private static final int DEFAULT_TIMERANGE = 5; /** * Number of layout columns in the main composite of this page. */ private static final int NUM_LAYOUT_COLUMNS = 4; /** * Initial threshold value (used for editing mode). */ private final Double initialThreshold; /** * Initial indicator whether the threshold is used as lower threshold. */ private final boolean initialLowerThreshold; /** * Initial time range value (used for editing mode). */ private final long initialTimerange; /** * Initial list of e-mail addresses (used for editing mode). */ private final List<String> initialsEmails; /** * Input field for the threshold value. */ private Text thresholdBox; /** * Input Spinner for the time range value in minutes. */ private Spinner timerangeSpinner; /** * Input field for email addresses. */ private StyledText emailsBox; /** * Checkbox for the selection of the threshold type (lower vs. upper). */ private Button lowerThresholdCheckBox; /** * Default Constructor. * * To be used for creation mode. */ public AlertDetailsWizardPage() { this(null, false, DEFAULT_TIMERANGE, null); } /** * Constructor. * * To be used for editing mode. * * @param initialThreshold * Initial threshold value (used for editing mode). * @param initialLowerThreshold * Initial indicator whether the threshold is used as lower threshold. * @param initialTimerange * Initial time range value (used for editing mode). * @param initialsEmails * Initial list of e-mail addresses (used for editing mode). */ public AlertDetailsWizardPage(Double initialThreshold, boolean initialLowerThreshold, long initialTimerange, List<String> initialsEmails) { super(TITLE); setTitle(TITLE); setMessage(DEFAULT_MESSAGE); this.initialThreshold = initialThreshold; this.initialTimerange = initialTimerange; this.initialsEmails = initialsEmails; this.initialLowerThreshold = initialLowerThreshold; } /** * {@inheritDoc} */ @Override public void createControl(Composite parent) { // create main composite final Composite main = new Composite(parent, SWT.NONE); main.setLayout(new GridLayout(NUM_LAYOUT_COLUMNS, false)); // create threshold controls Label thresholdLabel = new Label(main, SWT.LEFT); thresholdLabel.setText("Alert threshold:"); thresholdLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); thresholdBox = new Text(main, SWT.BORDER); thresholdBox.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); // create threshold type controls lowerThresholdCheckBox = new Button(main, SWT.CHECK); lowerThresholdCheckBox.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, NUM_LAYOUT_COLUMNS - 3, 1)); lowerThresholdCheckBox.setText("Use as lower threshold"); Label infoLabelThreshold = new Label(main, SWT.RIGHT); infoLabelThreshold.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); infoLabelThreshold.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_INFORMATION)); infoLabelThreshold.setToolTipText(THRESHOLD_INFO_TEXT); // create time range controls Label timerangeLabel = new Label(main, SWT.LEFT); timerangeLabel.setText("Check interval [min]:"); timerangeLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); timerangeSpinner = new Spinner(main, SWT.BORDER); timerangeSpinner.setMinimum(1); timerangeSpinner.setMaximum(Integer.MAX_VALUE); timerangeSpinner.setIncrement(1); timerangeSpinner.setPageIncrement(10); timerangeSpinner.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, NUM_LAYOUT_COLUMNS - 2, 1)); Label infoLabelTimerange = new Label(main, SWT.RIGHT); infoLabelTimerange.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); infoLabelTimerange.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_INFORMATION)); infoLabelTimerange.setToolTipText(TIMERANGE_INFO_TEXT); // create email addresses controls Label emailsLabel = new Label(main, SWT.LEFT); emailsLabel.setText("Send alerts to the following e-mail addresses:"); emailsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, NUM_LAYOUT_COLUMNS - 1, 1)); Label infoLabel = new Label(main, SWT.RIGHT); infoLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); infoLabel.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_INFORMATION)); infoLabel.setToolTipText(EMAIL_INFO_TEXT); emailsBox = new StyledText(main, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); emailsBox.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, NUM_LAYOUT_COLUMNS, 1)); setupListeners(); initContents(); setControl(main); } /** * Sets the message based on the page contents. */ protected void setPageMessage() { if (thresholdBox.getText().isEmpty()) { setMessage("Threshold must not be empty!", ERROR); return; } if (timerangeSpinner.getText().isEmpty()) { setMessage("Check interval must not be empty!", ERROR); return; } Pair<Integer, String> emailsErrorMessage = checkEmailText(); if (null != emailsErrorMessage) { setMessage("The email address '" + emailsErrorMessage.getSecond() + "' in line " + emailsErrorMessage.getFirst() + " is not valid!", ERROR); return; } setMessage(DEFAULT_MESSAGE); } /** * {@inheritDoc} */ @Override public boolean isPageComplete() { return !thresholdBox.getText().isEmpty() && !timerangeSpinner.getText().isEmpty() && (checkEmailText() == null); } /** * Sets up control listeners. */ private void setupListeners() { thresholdBox.addVerifyListener(new VerifyListener() { // verifies whether input is a valid number @Override public void verifyText(VerifyEvent event) { Text text = (Text) event.getSource(); final String previousText = text.getText(); String newText = previousText.substring(0, event.start) + event.text + previousText.substring(event.end); if (!newText.isEmpty() && !NumberUtils.isNumber(newText)) { event.doit = false; } } }); LineNumbersStyleListener.apply(emailsBox); Listener pageCompletionListener = new Listener() { @Override public void handleEvent(Event event) { setPageComplete(isPageComplete()); setPageMessage(); } }; thresholdBox.addListener(SWT.Modify, pageCompletionListener); timerangeSpinner.addListener(SWT.Modify, pageCompletionListener); emailsBox.addListener(SWT.Modify, pageCompletionListener); } /** * Initializes the contents of all fields if there are initial values. */ private void initContents() { if (null != initialThreshold) { thresholdBox.setText(String.valueOf(initialThreshold)); } lowerThresholdCheckBox.setSelection(initialLowerThreshold); timerangeSpinner.setSelection((int) initialTimerange); if (null != initialsEmails) { String emailsText = ""; for (String email : initialsEmails) { if (!emailsText.isEmpty()) { emailsText += NEW_LINE; } emailsText += email; } emailsBox.setText(emailsText); } } /** * Validates the content of the email addresses input field checking whether the e-mails have a * correct syntax. * * @return Returns a integer-string pair indicating the line number and e-mail address that is * not correct. If all e-mail addresses have a correct syntax, then this method returns * <code>null</code>. */ private Pair<Integer, String> checkEmailText() { int i = 0; for (String address : getEmailAddresses()) { if (!address.isEmpty() && !EMailUtils.isValidEmailAddress(address)) { return new Pair<Integer, String>(i + 1, address); } i++; } return null; } /** * Returns the specified threshold value. * * @return Returns the specified threshold value. */ public double getThreshold() { return Double.parseDouble(thresholdBox.getText()); } /** * Returns the specified threshold type. * * @return Returns the specified threshold type. */ public ThresholdType getThresholdType() { return lowerThresholdCheckBox.getSelection() ? ThresholdType.LOWER_THRESHOLD : ThresholdType.UPPER_THRESHOLD; } /** * Returns the specified time range value. * * @return Returns the specified time range value. */ public long getTimerange() { return Long.parseLong(timerangeSpinner.getText()); } /** * Returns the specified email addresses. * * @return Returns the specified email addresses. */ public List<String> getEmailAddresses() { String[] array = emailsBox.getText().split(NEW_LINE); List<String> emailAddresses = new ArrayList<>(); for (String element : array) { String address = element.trim(); if (!address.isEmpty()) { emailAddresses.add(element.trim()); } } return emailAddresses; } }