Java tutorial
/* * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. 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.lambda.upload.wizard.page; import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newCombo; import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newControlDecoration; import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newFillingLabel; import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newGroup; import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newRadioButton; import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newText; import static com.amazonaws.eclipse.lambda.LambdaAnalytics.ATTR_NAME_CHANGE_SELECTION; import static com.amazonaws.eclipse.lambda.LambdaAnalytics.ATTR_VALUE_REGION_SELECTION_COMBO; import static com.amazonaws.eclipse.lambda.LambdaAnalytics.EVENT_TYPE_UPLOAD_FUNCTION_WIZARD; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.databinding.AggregateValidationStatus; import org.eclipse.core.databinding.Binding; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.PojoObservables; import org.eclipse.core.databinding.observable.ChangeEvent; import org.eclipse.core.databinding.observable.IChangeListener; 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.jface.databinding.swt.ISWTObservableValue; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.fieldassist.ControlDecoration; 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.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 com.amazonaws.eclipse.core.AwsToolkitCore; import com.amazonaws.eclipse.core.mobileanalytics.ToolkitAnalyticsManager; import com.amazonaws.eclipse.core.regions.Region; import com.amazonaws.eclipse.core.regions.RegionUtils; import com.amazonaws.eclipse.core.regions.ServiceAbbreviations; import com.amazonaws.eclipse.core.ui.CancelableThread; import com.amazonaws.eclipse.databinding.BooleanValidator; import com.amazonaws.eclipse.databinding.ChainValidator; import com.amazonaws.eclipse.databinding.DecorationChangeListener; import com.amazonaws.eclipse.databinding.NotEmptyValidator; import com.amazonaws.eclipse.databinding.NotInListValidator; import com.amazonaws.eclipse.lambda.LambdaPlugin; import com.amazonaws.eclipse.lambda.ServiceApiUtils; import com.amazonaws.eclipse.lambda.upload.wizard.model.UploadFunctionWizardDataModel; import com.amazonaws.services.lambda.AWSLambda; import com.amazonaws.services.lambda.model.FunctionConfiguration; public class TargetFunctionSelectionPage extends WizardPageWithOnEnterHook { /* Data model and binding */ private final UploadFunctionWizardDataModel dataModel; private final DataBindingContext bindingContext; private final AggregateValidationStatus aggregateValidationStatus; /* UI widgets */ // Select region private Combo regionCombo; // Select Java function private IObservableValue existingJavaFunctionLoaded = new WritableValue(); private IObservableSet existingJavaFunctionNames = new WritableSet(); private Button useExistingJavaFunctionRadioButton; private ISWTObservableValue useExistingJavaFunctionRadioButtonObservable; private Combo existingJavaFunctionNameCombo; private Button createNewJavaFunctionRadioButton; private ISWTObservableValue createNewJavaFunctionRadioButtonObservable; private Text newJavaFunctionNameText; private ControlDecoration newJavaFunctionNameDecoration; private ISWTObservableValue newJavaFunctionNameTextObservable; private final SelectionListener javaFunctionSectionRadioButtonSelectionListener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { radioButtonSelected(e.getSource()); runValidators(); } }; /* Other */ private AWSLambda lambdaClient; private LoadJavaFunctionsThread loadJavaFunctionsThread; /* Constants */ private static final String LOADING = "Loading..."; private static final String NONE_FOUND = "None found"; public TargetFunctionSelectionPage(UploadFunctionWizardDataModel dataModel) { super("Select Target Lambda Function"); setTitle("Select Target Lambda Function"); setDescription(""); setPageComplete(false); this.dataModel = dataModel; this.bindingContext = new DataBindingContext(); this.aggregateValidationStatus = new AggregateValidationStatus(bindingContext, AggregateValidationStatus.MAX_SEVERITY); } public void createControl(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(1, false)); createRegionSection(composite); createJavaFunctionSection(composite); bindControls(); initializeValidators(); initializeDefaults(); // Force refresh function list onRegionSelectionChange(); setControl(composite); setPageComplete(false); } private void createRegionSection(Composite composite) { Group regionGroup = newGroup(composite, "Select AWS Region"); regionGroup.setLayout(new GridLayout(1, false)); newFillingLabel(regionGroup, "Select the AWS region where your Lambda function is created."); Region initialRegion = null; Region lastDeploymentRegion = getLastDeploymentRegion(); regionCombo = newCombo(regionGroup); for (Region region : RegionUtils.getRegionsForService(ServiceAbbreviations.LAMBDA)) { regionCombo.add(region.getName()); regionCombo.setData(region.getName(), region); if (region.equals(lastDeploymentRegion)) { initialRegion = region; } } // Find the default region selection if (initialRegion == null) { if (RegionUtils.isServiceSupportedInCurrentRegion(ServiceAbbreviations.LAMBDA)) { initialRegion = RegionUtils.getCurrentRegion(); } else { initialRegion = RegionUtils.getRegion(LambdaPlugin.DEFAULT_REGION); } } regionCombo.setText(initialRegion.getName()); regionCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { trackRegionComboChangeSelection(); onRegionSelectionChange(); } }); } private Region getLastDeploymentRegion() { return this.dataModel.getProjectMetadataBeforeUpload() == null ? null : this.dataModel.getProjectMetadataBeforeUpload().getLastDeploymentRegion(); } private void createJavaFunctionSection(Composite composite) { Group javaFunctionGroup = newGroup(composite, "Select or create a Lambda function:"); javaFunctionGroup.setLayout(new GridLayout(2, false)); useExistingJavaFunctionRadioButton = newRadioButton(javaFunctionGroup, "Choose an existing Lambda function:", 1, true, javaFunctionSectionRadioButtonSelectionListener); existingJavaFunctionNameCombo = newCombo(javaFunctionGroup); existingJavaFunctionNameCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onExistingJavaFunctionSelectionChange(); } }); createNewJavaFunctionRadioButton = newRadioButton(javaFunctionGroup, "Create a new Lambda function:", 1, true, javaFunctionSectionRadioButtonSelectionListener); newJavaFunctionNameText = newText(javaFunctionGroup); newJavaFunctionNameDecoration = newControlDecoration(newJavaFunctionNameText, "Enter a new function name or select an existing function."); } private void onRegionSelectionChange() { Region region = (Region) regionCombo.getData(regionCombo.getText()); String endpoint = region.getServiceEndpoints().get(ServiceAbbreviations.LAMBDA); lambdaClient = AwsToolkitCore.getClientFactory().getLambdaClientByEndpoint(endpoint); dataModel.setRegion(region); refreshFunctions(); } private void onExistingJavaFunctionSelectionChange() { if (existingJavaFunctionLoaded.getValue().equals(Boolean.TRUE)) { Object selectedData = existingJavaFunctionNameCombo.getData(existingJavaFunctionNameCombo.getText()); FunctionConfiguration selectedFunction = (FunctionConfiguration) selectedData; dataModel.setExistingFunction(selectedFunction); } } private void refreshFunctions() { existingJavaFunctionLoaded.setValue(false); existingJavaFunctionNames.clear(); if (existingJavaFunctionNameCombo != null) { existingJavaFunctionNameCombo.setItems(new String[] { LOADING }); existingJavaFunctionNameCombo.select(0); } CancelableThread.cancelThread(loadJavaFunctionsThread); loadJavaFunctionsThread = new LoadJavaFunctionsThread(getLastDeploymentFunctionName()); loadJavaFunctionsThread.start(); } private String getLastDeploymentFunctionName() { return this.dataModel.getProjectMetadataBeforeUpload() == null ? null : this.dataModel.getProjectMetadataBeforeUpload().getLastDeploymentFunctionName(); } private void radioButtonSelected(Object source) { if (source == useExistingJavaFunctionRadioButton || source == createNewJavaFunctionRadioButton) { boolean isCreatingNewFunction = (Boolean) createNewJavaFunctionRadioButtonObservable.getValue(); existingJavaFunctionNameCombo.setEnabled(!isCreatingNewFunction); newJavaFunctionNameText.setEnabled(isCreatingNewFunction); } } private void bindControls() { useExistingJavaFunctionRadioButtonObservable = SWTObservables .observeSelection(useExistingJavaFunctionRadioButton); createNewJavaFunctionRadioButtonObservable = SWTObservables .observeSelection(createNewJavaFunctionRadioButton); bindingContext.bindValue(createNewJavaFunctionRadioButtonObservable, PojoObservables.observeValue(dataModel, UploadFunctionWizardDataModel.P_IS_CREATING_NEW_FUNCTION)); newJavaFunctionNameTextObservable = SWTObservables.observeText(newJavaFunctionNameText, SWT.Modify); bindingContext.bindValue(newJavaFunctionNameTextObservable, PojoObservables.observeValue(dataModel, UploadFunctionWizardDataModel.P_NEW_FUNCTION_NAME)); } private void initializeValidators() { // Bind the validation status to the wizard page message aggregateValidationStatus.addChangeListener(new IChangeListener() { public void handleChange(ChangeEvent arg0) { Object value = aggregateValidationStatus.getValue(); if (value instanceof IStatus == false) return; IStatus status = (IStatus) value; boolean success = (status.getSeverity() == IStatus.OK); setPageComplete(success); if (success) { setMessage("", IStatus.OK); } else { setMessage(status.getMessage(), IStatus.ERROR); } } }); // Validation status providers bindingContext.addValidationStatusProvider(new ChainValidator<Boolean>(existingJavaFunctionLoaded, useExistingJavaFunctionRadioButtonObservable, // enabler new BooleanValidator("Please select a Lambda function"))); ChainValidator<String> functionNameValidator = new ChainValidator<String>(newJavaFunctionNameTextObservable, createNewJavaFunctionRadioButtonObservable, // enabler new NotEmptyValidator("Please provide a Lambda function name"), new NotInListValidator<String>(existingJavaFunctionNames, "Duplidate Lambda function name")); bindingContext.addValidationStatusProvider(functionNameValidator); new DecorationChangeListener(newJavaFunctionNameDecoration, functionNameValidator.getValidationStatus()); } private void runValidators() { Iterator<?> iterator = bindingContext.getBindings().iterator(); while (iterator.hasNext()) { Binding binding = (Binding) iterator.next(); binding.updateTargetToModel(); } } private void initializeDefaults() { existingJavaFunctionLoaded.setValue(false); useExistingJavaFunctionRadioButtonObservable.setValue(true); createNewJavaFunctionRadioButtonObservable.setValue(false); newJavaFunctionNameTextObservable.setValue("MyFunction"); radioButtonSelected(useExistingJavaFunctionRadioButton); } private final class LoadJavaFunctionsThread extends CancelableThread { private final String defaultFunctionName; /** * @param defaultFunctionName * the function that should be selected by default after all * functions are loaded. */ LoadJavaFunctionsThread(String defaultFunctionName) { this.defaultFunctionName = defaultFunctionName; } @Override public void run() { final List<String> javaFunctionNames = new ArrayList<String>(); final Map<String, FunctionConfiguration> javaFunctions = new HashMap<String, FunctionConfiguration>(); try { for (FunctionConfiguration funcConfig : ServiceApiUtils.getAllJavaFunctions(lambdaClient)) { javaFunctionNames.add(funcConfig.getFunctionName()); javaFunctions.put(funcConfig.getFunctionName(), funcConfig); } // Sort by name Collections.sort(javaFunctionNames); } catch (Exception e) { LambdaPlugin.getDefault().reportException("Unable to load existing Java functions.", e); setRunning(false); return; } Display.getDefault().asyncExec(new Runnable() { public void run() { try { synchronized (LoadJavaFunctionsThread.this) { if (!isCanceled()) { existingJavaFunctionNameCombo.removeAll(); if (javaFunctionNames.size() > 0) { existingJavaFunctionNames.clear(); existingJavaFunctionNames.addAll(javaFunctionNames); existingJavaFunctionLoaded.setValue(true); for (String funcName : javaFunctionNames) { existingJavaFunctionNameCombo.add(funcName); existingJavaFunctionNameCombo.setData(funcName, javaFunctions.get(funcName)); } useExistingJavaFunctionRadioButton.setEnabled(true); existingJavaFunctionNameCombo.setEnabled(true); existingJavaFunctionNameCombo.select(findDefaultFunction(javaFunctionNames)); onExistingJavaFunctionSelectionChange(); } else { existingJavaFunctionLoaded.setValue(false); useExistingJavaFunctionRadioButton.setEnabled(false); existingJavaFunctionNameCombo.setEnabled(false); existingJavaFunctionNameCombo.setItems(new String[] { NONE_FOUND }); existingJavaFunctionNameCombo.select(0); useExistingJavaFunctionRadioButtonObservable.setValue(false); createNewJavaFunctionRadioButtonObservable.setValue(true); radioButtonSelected(useExistingJavaFunctionRadioButton); } // Re-calculate UI layout ((Composite) getControl()).layout(); } } } finally { setRunning(false); } } }); } private int findDefaultFunction(List<String> functionNames) { if (this.defaultFunctionName == null) { return 0; } int defaultInd = functionNames.indexOf(this.defaultFunctionName); return defaultInd < 0 ? 0 : defaultInd; } } @Override protected void onEnterPage() { } /* * Analytics */ private void trackRegionComboChangeSelection() { ToolkitAnalyticsManager analytics = AwsToolkitCore.getDefault().getAnalyticsManager(); analytics.publishEvent(analytics.eventBuilder().setEventType(EVENT_TYPE_UPLOAD_FUNCTION_WIZARD) .addAttribute(ATTR_NAME_CHANGE_SELECTION, ATTR_VALUE_REGION_SELECTION_COMBO).build()); } }