/******************************************************************************* * Copyright (c) 2012 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.express.internal.ui.portforward; import java.util.List; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.core.databinding.conversion.Converter; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; import org.eclipse.core.databinding.validation.MultiValidator; import org.eclipse.core.databinding.validation.ValidationStatus; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.ViewerCell; 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.Table; import org.jboss.tools.common.ui.WizardUtils; import org.jboss.tools.common.ui.databinding.InvertingBooleanConverter; import org.jboss.tools.common.ui.databinding.ValueBindingBuilder; import org.jboss.tools.openshift.express.internal.ui.ExpressUIActivator; import org.jboss.tools.openshift.express.internal.ui.utils.Logger; import org.jboss.tools.openshift.internal.common.ui.wizard.AbstractOpenShiftWizardPage; import com.openshift.client.IApplicationPortForwarding; import com.openshift.client.OpenShiftSSHOperationException; /** * @author Xavier Coulon */ public class PortForwardingWizardPage extends AbstractOpenShiftWizardPage { private final PortForwardingWizardModel wizardModel; private TableViewer viewer; private Button refreshButton; private Button startButton; private Button stopButton; public PortForwardingWizardPage(final PortForwardingWizardModel wizardModel, final PortForwardingWizard portForwardingWizard) { super("Application port forwarding", null, "IApplicationPortForwardingingPage", portForwardingWizard); this.wizardModel = wizardModel; setDescription(NLS.bind("Please configure port forwarding for the {0} application", wizardModel.getApplication().getName())); } @Override protected void doCreateControls(Composite parent, DataBindingContext dbc) { GridLayoutFactory.fillDefaults().margins(6, 6).applyTo(parent); Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container); Composite tableContainer = new Composite(container, SWT.NONE); this.viewer = createTable(tableContainer, dbc); GridDataFactory.fillDefaults().span(1, 3).align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableContainer); refreshButton = new Button(container, SWT.PUSH); refreshButton.setText("Refresh"); GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).align(SWT.FILL, SWT.TOP).applyTo(refreshButton); refreshButton.addSelectionListener(onRefreshPorts()); startButton = new Button(container, SWT.PUSH); startButton.setText("Start All"); startButton.setEnabled(wizardModel.hasForwardablePorts()); GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).align(SWT.FILL, SWT.TOP).applyTo(startButton); startButton.addSelectionListener(onStartPortForwarding()); stopButton = new Button(container, SWT.PUSH); stopButton.setText("Stop All"); stopButton.setEnabled(false); GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).align(SWT.FILL, SWT.TOP).applyTo(stopButton); stopButton.addSelectionListener(onStopPortForwarding()); // checkbox to use the default "127.0.0.1" local IP address final Button useLocalIpAddressButton = new Button(container, SWT.CHECK); useLocalIpAddressButton.setText("Use '127.0.0.1' as the local address for all Services"); GridDataFactory.fillDefaults().span(2, 1).align(SWT.FILL, SWT.CENTER).grab(false, false) .applyTo(useLocalIpAddressButton); final IObservableValue useLocalIpAddressObservable = BeanProperties.value( PortForwardingWizardModel.PROPERTY_USE_DEFAULT_LOCAL_IP_ADDRESS).observe(wizardModel); final IObservableValue useLocalIpAddressButtonSelection = WidgetProperties.selection().observe( useLocalIpAddressButton); dbc.bindValue(useLocalIpAddressButtonSelection, useLocalIpAddressObservable); useLocalIpAddressObservable.addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(ValueChangeEvent event) { refreshViewerInput(wizardModel.getForwardablePorts()); } }); // checkbox to use the default "127.0.0.1" local IP address final Button findFreesPortButton = new Button(container, SWT.CHECK); findFreesPortButton.setText("Find free ports for all Services"); GridDataFactory.fillDefaults().span(2, 1).align(SWT.FILL, SWT.CENTER).grab(false, false) .applyTo(findFreesPortButton); final IObservableValue findFreePortsButtonObservable = BeanProperties.value( PortForwardingWizardModel.PROPERTY_USE_FREE_PORTS).observe(wizardModel); final IObservableValue findFreePortsButtonSelection = WidgetProperties.selection().observe(findFreesPortButton); dbc.bindValue(findFreePortsButtonSelection, findFreePortsButtonObservable); findFreePortsButtonObservable.addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(ValueChangeEvent event) { refreshViewerInput(wizardModel.getForwardablePorts()); } }); // enabling/disabling controls IObservableValue portForwardingStartedObservable = BeanProperties.value( PortForwardingWizardModel.PROPERTY_PORT_FORWARDING).observe(wizardModel); IObservableValue forwardablePortsExistObservable = BeanProperties.value( PortForwardingWizardModel.PROPERTY_FORWARDABLE_PORTS).observe(wizardModel); ValueBindingBuilder.bind(WidgetProperties.enabled().observe(startButton)) .notUpdating(portForwardingStartedObservable).converting(new InvertingBooleanConverter()).in(dbc); ValueBindingBuilder.bind(WidgetProperties.enabled().observe(startButton)) .notUpdating(forwardablePortsExistObservable).converting(new Converter(List.class, Boolean.class) { @Override public Object convert(Object fromObject) { if(fromObject instanceof List<?>) { return !((List<?>)fromObject).isEmpty(); } return Boolean.FALSE; } }).in(dbc); ValueBindingBuilder.bind(WidgetProperties.enabled().observe(stopButton)) .notUpdating(portForwardingStartedObservable).in(dbc); ValueBindingBuilder.bind(WidgetProperties.enabled().observe(useLocalIpAddressButton)) .notUpdating(portForwardingStartedObservable).converting(new InvertingBooleanConverter()).in(dbc); ValueBindingBuilder.bind(WidgetProperties.enabled().observe(findFreesPortButton)) .notUpdating(portForwardingStartedObservable).converting(new InvertingBooleanConverter()).in(dbc); } private SelectionListener onRefreshPorts() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { try { WizardUtils.runInWizard(new Job("Refreshing list of ports...") { @Override protected IStatus run(IProgressMonitor monitor) { try { if(!wizardModel.verifyApplicationSSHSession()) { return Status.CANCEL_STATUS; } wizardModel.refreshForwardablePorts(); refreshViewerInput(wizardModel.getForwardablePorts()); } catch (OpenShiftSSHOperationException e) { Logger.error("Failed to refresh list of ports", e); } return Status.OK_STATUS; } }, getContainer(), getDataBindingContext()); } catch (Exception exception) { // ignore } } }; } private SelectionListener onStartPortForwarding() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { try { WizardUtils.runInWizard(new Job("Starting Port-forwarding...") { @Override protected IStatus run(IProgressMonitor monitor) { try { if(!wizardModel.verifyApplicationSSHSession()) { return Status.CANCEL_STATUS; } wizardModel.startPortForwarding(); refreshViewerInput(wizardModel.getForwardablePorts()); } catch (OpenShiftSSHOperationException e) { return ExpressUIActivator.createErrorStatus("Failed to start port-forwarding.", e); } try { if(wizardModel.getApplication().isPortFowardingStarted()) { List<IApplicationPortForwarding> forwardablePorts = wizardModel.getForwardablePorts(); for (IApplicationPortForwarding portfwd : forwardablePorts) { if (!portfwd.isStarted(wizardModel.getApplication().getSSHSession())) { return ExpressUIActivator.createErrorStatus("Failed to start port-forwarding for one or more ports. See console for errors."); } } } else { return ExpressUIActivator.createErrorStatus("Failed to start port-forwarding. See console for errors."); } } catch (OpenShiftSSHOperationException e) { return ExpressUIActivator.createErrorStatus("Problem checking port forward status during start. See console for errors."); } return Status.OK_STATUS; } }, getContainer(), getDataBindingContext()); } catch (Exception exception) { // ignore } } }; } private SelectionListener onStopPortForwarding() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { try { WizardUtils.runInWizard(new Job("Stoppping Port-forwarding...") { @Override protected IStatus run(IProgressMonitor monitor) { try { if(!wizardModel.verifyApplicationSSHSession()) { return Status.CANCEL_STATUS; } wizardModel.stopPortForwarding(); refreshViewerInput(wizardModel.getForwardablePorts()); } catch (OpenShiftSSHOperationException e) { return ExpressUIActivator.createErrorStatus("Failed to stop port-forwarding.", e); } return Status.OK_STATUS; } }, getContainer(), getDataBindingContext()); } catch (Exception exception) { // ignore } } }; } protected TableViewer createTable(Composite tableContainer, DataBindingContext dbc) { Table table = new Table(tableContainer, SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL); table.setLinesVisible(true); table.setHeaderVisible(true); TableColumnLayout tableLayout = new TableColumnLayout(); tableContainer.setLayout(tableLayout); TableViewer viewer = new TableViewer(table); viewer.setContentProvider(new ArrayContentProvider()); createTableColumn("Service", 1, new CellLabelProvider() { @Override public void update(ViewerCell cell) { IApplicationPortForwarding port = (IApplicationPortForwarding) cell.getElement(); cell.setText(port.getName()); } }, viewer, tableLayout); createTableColumn("Local Address", 2, new CellLabelProvider() { @Override public void update(ViewerCell cell) { IApplicationPortForwarding port = (IApplicationPortForwarding) cell.getElement(); cell.setText(port.getLocalAddress()); } }, viewer, tableLayout); createTableColumn("Local Port", 2, new CellLabelProvider() { @Override public void update(ViewerCell cell) { IApplicationPortForwarding port = (IApplicationPortForwarding) cell.getElement(); cell.setText(Integer.toString(port.getLocalPort())); } }, viewer, tableLayout); createTableColumn("Remote Address", 2, new CellLabelProvider() { @Override public void update(ViewerCell cell) { IApplicationPortForwarding port = (IApplicationPortForwarding) cell.getElement(); cell.setText(port.getRemoteAddress()); } }, viewer, tableLayout); createTableColumn("Remote Port", 2, new CellLabelProvider() { @Override public void update(ViewerCell cell) { IApplicationPortForwarding port = (IApplicationPortForwarding) cell.getElement(); cell.setText(Integer.toString(port.getRemotePort())); } }, viewer, tableLayout); createTableColumn("Status", 1, new CellLabelProvider() { @Override public void update(ViewerCell cell) { IApplicationPortForwarding port = (IApplicationPortForwarding) cell.getElement(); try { final boolean started = port.isStarted(wizardModel.getApplication().getSSHSession()); cell.setText(started ? "Started" : "Stopped"); } catch (OpenShiftSSHOperationException e) { cell.setText("Unknown"); } } }, viewer, tableLayout); IObservableValue forwardablePortsModelObservable = BeanProperties.value(PortForwardingWizardModel.PROPERTY_FORWARDABLE_PORTS) .observe(wizardModel); final ForwardablePortListValidator validator = new ForwardablePortListValidator(forwardablePortsModelObservable); dbc.addValidationStatusProvider(validator); return viewer; } private void createTableColumn(String name, int weight, CellLabelProvider cellLabelProvider, TableViewer viewer, TableColumnLayout layout) { TableViewerColumn column = new TableViewerColumn(viewer, SWT.LEFT); column.getColumn().setText(name); column.setLabelProvider(cellLabelProvider); layout.setColumnData(column.getColumn(), new ColumnWeightData(weight, true)); } @Override protected void onPageActivated(DataBindingContext dbc) { final Job j = new Job("Loading application's forwardable ports...") { @Override protected IStatus run(IProgressMonitor monitor) { try { monitor.beginTask("Checking Application SSH session...", 1); wizardModel.verifyApplicationSSHSession(); monitor.worked(1); monitor.beginTask("Retrieving ports...", 1); wizardModel.loadForwardablePorts(); refreshViewerInput(wizardModel.getForwardablePorts()); monitor.worked(1); return Status.OK_STATUS; } catch (OpenShiftSSHOperationException e) { return ExpressUIActivator.createErrorStatus( "Could not load forwardable ports for application ''{0}''", e, wizardModel.getApplication().getName()); } } }; try { WizardUtils.runInWizard(j, getContainer(), getDataBindingContext()); } catch (Exception e) { Logger.error("Failed to load application's forwardable ports", e); } } private void refreshViewerInput(List<IApplicationPortForwarding> ports) { getShell().getDisplay().syncExec(new Runnable() { @Override public void run() { try { viewer.setInput(wizardModel.getForwardablePorts()); } catch (Exception e) { Logger.error("Failed to refresh table content with list of ports for selected application", e); } } }); } class ForwardablePortListValidator extends MultiValidator { private final IObservableValue viewerObservable; public ForwardablePortListValidator(IObservableValue viewerObservable) { this.viewerObservable = viewerObservable; } @Override protected IStatus validate() { @SuppressWarnings("unchecked") final List<IApplicationPortForwarding> ports = (List<IApplicationPortForwarding>) viewerObservable.getValue(); if(ports == null || ports.isEmpty()) { return ValidationStatus.error("There are no available ports to forward for this application.\nYour application may be stopped."); } return Status.OK_STATUS; } } @Override public boolean isPageComplete() { // enable OK button even when binding validation errors exist return true; } }