Java tutorial
/******************************************************************************* * Copyright (c) 2015 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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 * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.openshift.internal.ui.wizard.deployimage; import java.util.List; import java.util.Random; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.validation.MultiValidator; import org.eclipse.core.databinding.validation.ValidationStatus; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; import org.eclipse.jface.databinding.viewers.ViewerProperties; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.window.Window; import org.eclipse.jface.wizard.IWizard; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Table; import org.jboss.tools.common.ui.databinding.ValueBindingBuilder; import org.jboss.tools.openshift.internal.common.ui.databinding.IsNotNull2BooleanConverter; import org.jboss.tools.openshift.internal.common.ui.utils.TableViewerBuilder; import org.jboss.tools.openshift.internal.common.ui.utils.TableViewerBuilder.IColumnLabelProvider; import org.jboss.tools.openshift.internal.common.ui.utils.UIUtils; import org.jboss.tools.openshift.internal.common.ui.wizard.AbstractOpenShiftWizardPage; import com.openshift.restclient.model.IServicePort; /** * Page to configure OpenShift services and routes * * @author jeff.cantrill * */ public class ServicesAndRoutingPage extends AbstractOpenShiftWizardPage { private static final String PAGE_NAME = "Services && Routing Settings Page"; private static final String PAGE_TITLE = "Services && Routing Settings"; private static final String PAGE_DESCRIPTION = ""; private IServiceAndRoutingPageModel model; TableViewer portsViewer; public ServicesAndRoutingPage(IWizard wizard, IServiceAndRoutingPageModel model) { super(PAGE_TITLE, PAGE_DESCRIPTION, PAGE_NAME, wizard); this.model = model; } @Override protected void doCreateControls(Composite parent, DataBindingContext dbc) { GridLayoutFactory.fillDefaults().margins(10, 10).applyTo(parent); createExposedPortsControl(parent, dbc); //routing Composite routingContainer = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(routingContainer); GridLayoutFactory.fillDefaults().margins(6, 6).numColumns(2).applyTo(routingContainer); Button btnAddRoute = new Button(routingContainer, SWT.CHECK); btnAddRoute.setText("Add Route"); btnAddRoute.setToolTipText( "Adding a route to the service will make the image accessible\noutside of the OpenShift cluster on all the available service ports. \nYou can target a specific port by editing the route later."); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(false, false).applyTo(btnAddRoute); ValueBindingBuilder.bind(WidgetProperties.selection().observe(btnAddRoute)) .to(BeanProperties.value(IServiceAndRoutingPageModel.PROPERTY_ADD_ROUTE).observe(model)).in(dbc); } private void createExposedPortsControl(Composite parent, DataBindingContext dbc) { Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(2).margins(6, 6).applyTo(container); Label label = new Label(container, SWT.NONE); label.setText("Service Ports:"); label.setToolTipText("The exposed ports of the image."); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(2, 1).applyTo(label); Composite tableContainer = new Composite(container, SWT.NONE); portsViewer = createTable(tableContainer); GridDataFactory.fillDefaults().span(1, 5).align(SWT.FILL, SWT.FILL).grab(true, true) .applyTo(tableContainer); ValueBindingBuilder.bind(ViewerProperties.singleSelection().observe(portsViewer)) .to(BeanProperties.value(IServiceAndRoutingPageModel.PROPERTY_SELECTED_SERVICE_PORT).observe(model)) .in(dbc); portsViewer.setContentProvider(new ObservableListContentProvider()); IObservableList portsObservable = BeanProperties.list(IServiceAndRoutingPageModel.PROPERTY_SERVICE_PORTS) .observe(model); portsViewer.setInput(portsObservable); dbc.addValidationStatusProvider(new MultiValidator() { @Override protected IStatus validate() { if (portsObservable.isEmpty()) { return ValidationStatus.error( "At least 1 port is required when generating the service for the deployed image"); } return Status.OK_STATUS; } }); Button btnAdd = new Button(container, SWT.PUSH); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).applyTo(btnAdd); btnAdd.setText("Add..."); btnAdd.setToolTipText( "Add a port to be exposed by the service which is not explicilty declared by the image."); btnAdd.addSelectionListener(onAdd()); UIUtils.setDefaultButtonWidth(btnAdd); Button btnEdit = new Button(container, SWT.PUSH); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).applyTo(btnEdit); btnEdit.setText("Edit..."); btnEdit.setToolTipText("Edit a port to be exposed by the service."); btnEdit.addSelectionListener(new EditHandler()); ValueBindingBuilder.bind(WidgetProperties.enabled().observe(btnEdit)).notUpdatingParticipant() .to(BeanProperties.value(IServiceAndRoutingPageModel.PROPERTY_SELECTED_SERVICE_PORT).observe(model)) .converting(new IsNotNull2BooleanConverter()).in(dbc); UIUtils.setDefaultButtonWidth(btnEdit); Button removeButton = new Button(container, SWT.PUSH); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).applyTo(removeButton); removeButton.setText("Remove..."); removeButton.setToolTipText("Remove a port that will be exposed by the service."); removeButton.addSelectionListener(onRemove()); ValueBindingBuilder.bind(WidgetProperties.enabled().observe(removeButton)).notUpdatingParticipant() .to(BeanProperties.value(IServiceAndRoutingPageModel.PROPERTY_SELECTED_SERVICE_PORT).observe(model)) .converting(new IsNotNull2BooleanConverter()).in(dbc); UIUtils.setDefaultButtonWidth(removeButton); Button btnReset = new Button(container, SWT.PUSH); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).applyTo(btnReset); btnReset.setText("Reset"); btnReset.setToolTipText("Resets the list of ports to the exposed ports of the image."); btnReset.addSelectionListener(onReset()); UIUtils.setDefaultButtonWidth(btnReset); } private SelectionListener onAdd() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String message = "Add a port to be exposed by the service"; ServicePortAdapter port = new ServicePortAdapter(); List<IServicePort> servicePorts = model.getServicePorts(); if (!servicePorts.isEmpty()) { Set<Integer> ports = servicePorts.stream().map((p) -> p.getPort()).collect(Collectors.toSet()); port.setPort(generateNewPort(ports)); ports = servicePorts.stream().map((p) -> getIntegerPort(p.getTargetPort())) .collect(Collectors.toSet()); port.setTargetPort(generateNewPort(ports)); } ServicePortDialog dialog = new ServicePortDialog(port, message, servicePorts); if (Window.OK == dialog.open()) { port.setName(NLS.bind("{0}-tcp", port.getPort())); model.addServicePort(port); model.setSelectedServicePort(port); } } }; } private static final Random seed = new Random(); private static int generateNewPort(Set<Integer> ports) { while (true) { int n = 1025 + seed.nextInt(65536 - 1025); // port should be in range [1025..65535] if (!ports.contains(n)) { return n; } } } /** * Returns integer if string value is an integer in range [0..65535], or -1. * @param stringPort * @return */ private static int getIntegerPort(String stringPort) { if (StringUtils.isBlank(stringPort) || stringPort.length() > 5 || !StringUtils.isNumeric(stringPort)) { return -1; } //Now, string port has only digits, and it length is no more than 5. There may be no NumberFormatException. int value = Integer.parseInt(stringPort); return (value > 65535) ? -1 : value; } class EditHandler extends SelectionAdapter implements IDoubleClickListener { @Override public void doubleClick(DoubleClickEvent event) { handleEvent(); } @Override public void widgetSelected(SelectionEvent e) { handleEvent(); } public void handleEvent() { String message = "Edit the port to be exposed by the service"; final IServicePort port = model.getSelectedServicePort(); final ServicePortAdapter target = new ServicePortAdapter(port); ServicePortDialog dialog = new ServicePortDialog(target, message, model.getServicePorts()); if (Window.OK == dialog.open()) { target.setName(NLS.bind("{0}-tcp", target.getPort())); model.updateServicePort(port, target); model.setSelectedServicePort(target); } } } protected TableViewer createTable(Composite tableContainer) { Table table = new Table(tableContainer, SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL); table.setLinesVisible(true); table.setHeaderVisible(true); TableViewer viewer = new TableViewerBuilder(table, tableContainer) .contentProvider(new ArrayContentProvider()).column(new IColumnLabelProvider<IServicePort>() { @Override public String getValue(IServicePort port) { return port.getName(); } }).name("Name").align(SWT.LEFT).weight(2).minWidth(50).buildColumn() .column(new IColumnLabelProvider<IServicePort>() { @Override public String getValue(IServicePort port) { return "" + port.getPort(); } }).name("Service Port").align(SWT.LEFT).weight(1).minWidth(25).buildColumn() .column(new IColumnLabelProvider<IServicePort>() { @Override public String getValue(IServicePort port) { return port.getTargetPort(); } }).name("Pod Port").align(SWT.LEFT).weight(1).minWidth(25).buildColumn().buildViewer(); viewer.addDoubleClickListener(new EditHandler()); return viewer; } private SelectionListener onRemove() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { IServicePort port = UIUtils.getFirstElement(portsViewer.getSelection(), IServicePort.class); if (MessageDialog.openQuestion(getShell(), "Remove port", NLS.bind("Are you sure you want to delete the port {0}?", port.getPort()))) { model.removeServicePort(port); portsViewer.refresh(); } } }; } private SelectionListener onReset() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (MessageDialog.openQuestion(getShell(), "Reset ports", "Are you sure you want to reset the serviced ports to those exposed by the image?")) { model.resetServicePorts(); portsViewer.refresh(); } } }; } }