Java tutorial
/* * Copyright 2010-2011 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://aws.amazon.com/apache2.0 * * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES * OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and * limitations under the License. */ package com.amazonaws.eclipse.elasticbeanstalk.server.ui; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.databinding.beans.PojoObservables; import org.eclipse.core.databinding.observable.set.IObservableSet; import org.eclipse.core.databinding.observable.set.WritableSet; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.databinding.swt.ISWTObservableValue; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.statushandlers.StatusManager; import org.eclipse.wst.server.ui.wizard.IWizardHandle; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.eclipse.core.AwsToolkitCore; import com.amazonaws.eclipse.elasticbeanstalk.ElasticBeanstalkPlugin; import com.amazonaws.eclipse.elasticbeanstalk.Region; import com.amazonaws.eclipse.elasticbeanstalk.deploy.DeployWizardDataModel; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.databinding.BooleanValidator; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.databinding.ChainValidator; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.databinding.DecorationChangeListener; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.databinding.NoInvalidNameCharactersValidator; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.databinding.NotEmptyValidator; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.databinding.NotInListValidator; import com.amazonaws.services.elasticbeanstalk.AWSElasticBeanstalk; import com.amazonaws.services.elasticbeanstalk.model.ApplicationDescription; import com.amazonaws.services.elasticbeanstalk.model.EnvironmentDescription; import com.amazonaws.services.elasticbeanstalk.model.EnvironmentStatus; final class DeployWizardApplicationSelectionPage extends AbstractDeployWizardPage { private static final String LOADING = "Loading..."; private static final String NONE_FOUND = "None found"; // Region controls private Combo regionCombo; // Application controls private Button createNewApplicationRadioButton; private ISWTObservableValue createNewApplicationRadioButtonObservable; private Combo existingApplicationCombo; private Text newApplicationDescriptionText; private Text newApplicationNameText; private ControlDecoration newApplicationNameDecoration; private Button existingApplicationRadioButton; // Environment controls private ControlDecoration newEnvironmentNameDecoration; private Text newEnvironmentNameText; private Text newEnvironmentDescriptionText; // Asynchronous workers private LoadApplicationsThread loadApplicationsThread; private LoadEnvironmentsThread loadEnvironmentsThread; private IObservableSet existingEnvironmentNames = new WritableSet(); private IObservableSet existingApplicationNames = new WritableSet(); private IObservableValue environmentNamesLoaded = new WritableValue(); private IObservableValue applicationNamesLoaded = new WritableValue(); private ISWTObservableValue newApplicationNameTextObservable; private ISWTObservableValue newApplicationDescriptionTextObservable; private ISWTObservableValue newEnvironmentDescriptionTextObservable; private ISWTObservableValue newEnvironmentNameTextObservable; // Status of our connectivity to AWS Elastic Beanstalk private IStatus connectionStatus; private AWSElasticBeanstalk elasticBeanstalkClient; DeployWizardApplicationSelectionPage(DeployWizardDataModel wizardDataModel) { super(wizardDataModel); setComplete(false); environmentNamesLoaded.setValue(false); applicationNamesLoaded.setValue(false); } /* (non-Javadoc) * @see org.eclipse.wst.server.ui.wizard.WizardFragment#createComposite(org.eclipse.swt.widgets.Composite, org.eclipse.wst.server.ui.wizard.IWizardHandle) */ @Override public Composite createComposite(Composite parent, IWizardHandle handle) { wizardHandle = handle; elasticBeanstalkClient = AwsToolkitCore.getClientFactory() .getElasticBeanstalkClientByEndpoint(wizardDataModel.getRegionEndpoint()); handle.setImageDescriptor( AwsToolkitCore.getDefault().getImageRegistry().getDescriptor(AwsToolkitCore.IMAGE_AWS_LOGO)); handle.setMessage("", IStatus.OK); connectionStatus = testConnection(); if (connectionStatus.isOK()) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(2, false)); createRegionSection(composite); createApplicationSection(composite); createEnvironmentSection(composite); createImportSection(composite); bindControls(); initializeDefaults(); return composite; } else { return new ErrorComposite(parent, SWT.NONE, connectionStatus); } } private IStatus testConnection() { try { wizardHandle.setMessage("", IStatus.OK); wizardHandle.run(true, false, new CheckAccountRunnable(elasticBeanstalkClient)); wizardHandle.setMessage("", IStatus.OK); return Status.OK_STATUS; } catch (InvocationTargetException ite) { String errorMessage = "Unable to connect to AWS Elastic Beanstalk. "; try { throw ite.getCause(); } catch (AmazonServiceException ase) { errorMessage += "Make sure you've registered your AWS account for the AWS Elastic Beanstalk service."; } catch (AmazonClientException ace) { errorMessage += "Make sure your computer is connected to the internet, and any network firewalls or proxys are configured appropriately."; } catch (Throwable t) { } return new Status(IStatus.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, errorMessage, ite.getCause()); } catch (InterruptedException e) { return Status.CANCEL_STATUS; } } /** * Initializes the page to its default selections */ private void initializeDefaults() { createNewApplicationRadioButtonObservable.setValue(true); existingApplicationRadioButton.setSelection(false); newApplicationNameTextObservable.setValue(""); newApplicationDescriptionTextObservable.setValue(""); newEnvironmentNameTextObservable.setValue(""); newEnvironmentDescriptionTextObservable.setValue(""); // Trigger the standard enabled / disabled control logic radioButtonSelected(createNewApplicationRadioButton); } private void createRegionSection(Composite composite) { Group regionGroup = newGroup(composite, "", 2); regionGroup.setLayout(new GridLayout(2, false)); newLabel(regionGroup, "Region:"); regionCombo = newCombo(regionGroup); for (Region region : Region.values()) { regionCombo.add(region.getName()); regionCombo.setData(region.getName(), region); } Region region = Region.findByEndpoint(wizardDataModel.getRegionEndpoint()); if (region != null) regionCombo.setText(region.getName()); newFillingLabel(regionGroup, "AWS regions are geographically isolated, " + "allowing you to position your Elastic Beanstalk application closer to you or your customers.", 2); regionCombo.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { Region region = (Region) regionCombo.getData(regionCombo.getText()); elasticBeanstalkClient = AwsToolkitCore.getClientFactory() .getElasticBeanstalkClientByEndpoint(region.getEndpoint()); wizardDataModel.setRegionEndpoint(region.getEndpoint()); createNewApplicationRadioButtonObservable.setValue(true); existingApplicationCombo.setEnabled(false); newApplicationNameText.setEnabled(true); newApplicationDescriptionText.setEnabled(true); refreshApplications(); refreshEnvironments(); } public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } }); } private void createApplicationSection(Composite composite) { Group applicationGroup = newGroup(composite, "Application:", 2); applicationGroup.setLayout(new GridLayout(2, false)); createNewApplicationRadioButton = newRadioButton(applicationGroup, "Create a new application:", 2, true); createNewApplicationRadioButtonObservable = SWTObservables .observeSelection(createNewApplicationRadioButton); new NewApplicationOptionsComposite(applicationGroup); existingApplicationRadioButton = newRadioButton(applicationGroup, "Choose an existing application:", 1); existingApplicationCombo = newCombo(applicationGroup); existingApplicationCombo.setEnabled(false); } private void createEnvironmentSection(Composite composite) { Group environmentOptionsGroup = newGroup(composite, "Environment:", 2); environmentOptionsGroup.setLayout(new GridLayout(2, false)); new NewEnvironmentOptionsComposite(environmentOptionsGroup); } private void createImportSection(final Composite composite) { Hyperlink link = new Hyperlink(composite, SWT.None); link.addHyperlinkListener(new HyperlinkAdapter() { public void linkActivated(HyperlinkEvent e) { ImportEnvironmentsWizard newWizard = new ImportEnvironmentsWizard(); WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newWizard); dialog.open(); } }); link.setText("Import an existing environment into the Servers view"); link.setUnderlined(true); GridData layoutData = new GridData(); layoutData.horizontalSpan = 2; link.setLayoutData(layoutData); } private void bindControls() { super.initializeValidators(); newApplicationNameTextObservable = SWTObservables.observeText(newApplicationNameText, SWT.Modify); bindingContext.bindValue(newApplicationNameTextObservable, PojoObservables.observeValue(wizardDataModel, DeployWizardDataModel.NEW_APPLICATION_NAME), null, null); ChainValidator<String> applicationNameValidator = new ChainValidator<String>( newApplicationNameTextObservable, createNewApplicationRadioButtonObservable, new NotEmptyValidator("Application name cannot be empty."), new NoInvalidNameCharactersValidator("Invalid characters in application name."), new NotInListValidator<String>(existingApplicationNames, "Duplicate application name.")); bindingContext.addValidationStatusProvider(applicationNameValidator); bindingContext.addValidationStatusProvider(new ChainValidator<Boolean>(applicationNamesLoaded, null, new BooleanValidator("Appliction names not yet loaded"))); new DecorationChangeListener(newApplicationNameDecoration, applicationNameValidator.getValidationStatus()); newApplicationDescriptionTextObservable = SWTObservables.observeText(newApplicationDescriptionText, SWT.Modify); bindingContext.bindValue(newApplicationDescriptionTextObservable, PojoObservables.observeValue(wizardDataModel, DeployWizardDataModel.NEW_APPLICATION_DESCRIPTION), null, null); bindingContext.bindValue(createNewApplicationRadioButtonObservable, PojoObservables.observeValue(wizardDataModel, DeployWizardDataModel.CREATING_NEW_APPLICATION), null, null); // Existing application bindings bindingContext.bindValue(SWTObservables.observeSelection(existingApplicationCombo), PojoObservables.observeValue(wizardDataModel, DeployWizardDataModel.EXISTING_APPLICATION_NAME)); // New environment bindings newEnvironmentNameTextObservable = SWTObservables.observeText(newEnvironmentNameText, SWT.Modify); bindingContext.bindValue(newEnvironmentNameTextObservable, PojoObservables.observeValue(wizardDataModel, DeployWizardDataModel.NEW_ENVIRONMENT_NAME), null, null); newEnvironmentDescriptionTextObservable = SWTObservables.observeText(newEnvironmentDescriptionText, SWT.Modify); bindingContext.bindValue(newEnvironmentDescriptionTextObservable, PojoObservables.observeValue(wizardDataModel, DeployWizardDataModel.NEW_ENVIRONMENT_DESCRIPTION), null, null); ChainValidator<String> environmentNameValidator = new ChainValidator<String>( newEnvironmentNameTextObservable, null, new NotEmptyValidator("The environment name cannot be empty."), new NoInvalidNameCharactersValidator("Invalid characters in environment name."), new NotInListValidator<String>(existingEnvironmentNames, "Duplicate environment name.")); bindingContext.addValidationStatusProvider(environmentNameValidator); bindingContext.addValidationStatusProvider(new ChainValidator<Boolean>(environmentNamesLoaded, null, new BooleanValidator("Environment names not yet loaded"))); new DecorationChangeListener(newEnvironmentNameDecoration, environmentNameValidator.getValidationStatus()); } /** * Asynchronously updates the set of existing applications. Must be called * from the UI thread. */ private void refreshApplications() { /* * While the values load, we need to disable the controls and fake a * radio button selected event. */ createNewApplicationRadioButton.setSelection(true); existingApplicationRadioButton.setSelection(false); existingApplicationRadioButton.setEnabled(false); radioButtonSelected(createNewApplicationRadioButton); existingApplicationCombo.setItems(new String[] { LOADING }); existingApplicationCombo.select(0); cancelThread(loadApplicationsThread); applicationNamesLoaded.setValue(false); loadApplicationsThread = new LoadApplicationsThread(); loadApplicationsThread.start(); } /** * Safely cancels the thread given. */ private void cancelThread(CancelableThread thread) { if (thread != null) { synchronized (thread) { if (thread.isRunning()) { thread.cancel(); } } } } /** * Asynchronously updates the set of existing environments for the current * application. */ private void refreshEnvironments() { cancelThread(loadEnvironmentsThread); environmentNamesLoaded.setValue(false); loadEnvironmentsThread = new LoadEnvironmentsThread(); loadEnvironmentsThread.start(); } /** * We handle radio button selections by enabling and disabling various * controls. There are only two sources of these events that we care about. */ @Override protected void radioButtonSelected(Object source) { if (source == existingApplicationRadioButton || source == createNewApplicationRadioButton) { boolean isCreatingNewApplication = (Boolean) createNewApplicationRadioButtonObservable.getValue(); existingApplicationCombo.setEnabled(!isCreatingNewApplication); newApplicationNameText.setEnabled(isCreatingNewApplication); newApplicationDescriptionText.setEnabled(isCreatingNewApplication); } } @Override public void enter() { super.enter(); if (connectionStatus != null && connectionStatus.isOK()) { refreshApplications(); refreshEnvironments(); } } /** * Cancel any outstanding work before exiting. */ @Override public void exit() { cancelThread(loadApplicationsThread); cancelThread(loadEnvironmentsThread); super.exit(); } private boolean isServiceSignUpException(Exception e) { if (e instanceof AmazonServiceException) { AmazonServiceException ase = (AmazonServiceException) e; return "OptInRequired".equalsIgnoreCase(ase.getErrorCode()); } return false; } private IStatus newServiceSignUpErrorStatus(Exception e) { return new Status(Status.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, "Error connecting to AWS Elastic Beanstalk. " + "Make sure you've signed up your AWS account for Elastic Beanstalk, and " + "waited for the changes to propagate.", e); } private class NewApplicationOptionsComposite extends Composite { public NewApplicationOptionsComposite(Composite parent) { super(parent, SWT.NONE); setLayout(new GridLayout(2, false)); GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); gridData.horizontalIndent = 15; gridData.horizontalSpan = 2; setLayoutData(gridData); newLabel(this, "Name:"); newApplicationNameText = newText(this); newApplicationNameDecoration = newControlDecoration(newApplicationNameText, "Enter a new application name or select an existing application."); newLabel(this, "Description:"); newApplicationDescriptionText = newText(this); } } private class NewEnvironmentOptionsComposite extends Composite { public NewEnvironmentOptionsComposite(Composite parent) { super(parent, SWT.NONE); setLayout(new GridLayout(2, false)); GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); gridData.horizontalIndent = 15; gridData.horizontalSpan = 2; setLayoutData(gridData); newLabel(this, "Name:"); newEnvironmentNameText = newText(this); newEnvironmentNameDecoration = newControlDecoration(newEnvironmentNameText, "Enter a new environment name"); newLabel(this, "Description:"); newEnvironmentDescriptionText = newText(this); } } private final class LoadApplicationsThread extends CancelableThread { @Override public void run() { final List<ApplicationDescription> applications = new ArrayList<ApplicationDescription>(); try { applications.addAll(elasticBeanstalkClient.describeApplications().getApplications()); } catch (Exception e) { if (isServiceSignUpException(e)) { StatusManager.getManager().handle(newServiceSignUpErrorStatus(e), StatusManager.SHOW | StatusManager.LOG); } else { Status status = new Status(Status.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, "Unable to load existing applications: " + e.getMessage(), e); StatusManager.getManager().handle(status, StatusManager.LOG); } setRunning(false); return; } Display.getDefault().asyncExec(new Runnable() { public void run() { try { List<String> applicationNames = new ArrayList<String>(); for (ApplicationDescription application : applications) { applicationNames.add(application.getApplicationName()); } Collections.sort(applicationNames); synchronized (LoadApplicationsThread.this) { if (!isCanceled()) { existingApplicationNames.clear(); existingApplicationNames.addAll(applicationNames); existingApplicationCombo.removeAll(); for (String applicationName : applicationNames) { existingApplicationCombo.add(applicationName); } if (applications.size() > 0) { existingApplicationCombo.select(0); existingApplicationRadioButton.setEnabled(true); } else { existingApplicationCombo.setEnabled(false); existingApplicationRadioButton.setEnabled(false); createNewApplicationRadioButtonObservable.setValue(true); existingApplicationCombo.setItems(new String[] { NONE_FOUND }); existingApplicationCombo.select(0); } applicationNamesLoaded.setValue(true); runValidators(); } } } finally { setRunning(false); } } }); } } private final class LoadEnvironmentsThread extends CancelableThread { @Override public void run() { final List<EnvironmentDescription> environments = new ArrayList<EnvironmentDescription>(); try { environments.addAll(elasticBeanstalkClient.describeEnvironments().getEnvironments()); } catch (Exception e) { if (isServiceSignUpException(e)) { StatusManager.getManager().handle(newServiceSignUpErrorStatus(e), StatusManager.SHOW | StatusManager.LOG); } else { Status status = new Status(Status.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, "Unable to load existing environments: " + e.getMessage(), e); StatusManager.getManager().handle(status, StatusManager.LOG); } setRunning(false); return; } Display.getDefault().asyncExec(new Runnable() { public void run() { try { List<String> environmentNames = new ArrayList<String>(); for (EnvironmentDescription environment : environments) { // Skip any terminated environments, since we can safely reuse their names if (isEnvironmentTerminated(environment)) continue; environmentNames.add(environment.getEnvironmentName()); } Collections.sort(environmentNames); synchronized (LoadEnvironmentsThread.this) { if (!isCanceled()) { existingEnvironmentNames.clear(); existingEnvironmentNames.addAll(environmentNames); environmentNamesLoaded.setValue(true); runValidators(); } } } finally { setRunning(false); } } }); } } private boolean isEnvironmentTerminated(EnvironmentDescription environment) { if (environment == null || environment.getStatus() == null) return false; try { EnvironmentStatus status = EnvironmentStatus.valueOf(environment.getStatus()); return (status == EnvironmentStatus.Terminated); } catch (Exception e) { return false; } } @Override public String getPageTitle() { return "Configure Application and Environment"; } @Override public String getPageDescription() { return "Choose a name for your application and environment"; } }