/* * Copyright 2010-2012 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.configEditor; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.forms.ManagedForm; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.wst.server.ui.internal.ImageResource; import com.amazonaws.eclipse.core.AwsToolkitCore; import com.amazonaws.eclipse.ec2.Ec2Plugin; import com.amazonaws.eclipse.elasticbeanstalk.ConfigurationOptionConstants; import com.amazonaws.eclipse.elasticbeanstalk.ElasticBeanstalkPlugin; import com.amazonaws.eclipse.elasticbeanstalk.jobs.ExportConfigurationJob; import com.amazonaws.eclipse.elasticbeanstalk.jobs.UpdateEnvironmentConfigurationJob; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.configEditor.basic.AdvancedEnvironmentTypeConfigEditorSection; import com.amazonaws.services.elasticbeanstalk.AWSElasticBeanstalk; import com.amazonaws.services.elasticbeanstalk.model.ApplicationDescription; import com.amazonaws.services.elasticbeanstalk.model.ConfigurationOptionDescription; import com.amazonaws.services.elasticbeanstalk.model.ConfigurationOptionSetting; import com.amazonaws.services.elasticbeanstalk.model.DescribeApplicationsRequest; import com.amazonaws.services.elasticbeanstalk.model.DescribeApplicationsResult; import com.amazonaws.services.elasticbeanstalk.model.DescribeEnvironmentsRequest; import com.amazonaws.services.elasticbeanstalk.model.EnvironmentDescription; import com.amazonaws.services.elasticbeanstalk.model.EnvironmentStatus; import com.amazonaws.services.elasticbeanstalk.model.UpdateEnvironmentRequest; import com.amazonaws.services.elasticbeanstalk.model.ValidateConfigurationSettingsRequest; import com.amazonaws.services.elasticbeanstalk.model.ValidateConfigurationSettingsResult; import com.amazonaws.services.elasticbeanstalk.model.ValidationMessage; /** * Advanced environment configuration editor part. */ @SuppressWarnings("restriction") public class EnvironmentConfigEditorPart extends AbstractEnvironmentConfigEditorPart implements RefreshListener { private Action exportTemplateAction; private Action importTemplateAction; private Composite leftColumnComp; private Composite rightColumnComp; @Override public void init(IEditorSite site, IEditorInput input) { super.init(site, input); model.addRefreshListener(this); } @Override public void createPartControl(Composite parent) { managedForm = new ManagedForm(parent); setManagedForm(managedForm); ScrolledForm form = managedForm.getForm(); FormToolkit toolkit = managedForm.getToolkit(); toolkit.decorateFormHeading(form.getForm()); form.setText("Environment Configuration"); form.setImage(ImageResource.getImage(ImageResource.IMG_SERVER)); form.getBody().setLayout(new GridLayout()); Composite columnComp = toolkit.createComposite(form.getBody()); GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.horizontalSpacing = 10; columnComp.setLayout(layout); columnComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL)); Label restartNotice = toolkit.createLabel(columnComp, RESTART_NOTICE, SWT.WRAP); GridData layoutData = new GridData(SWT.FILL, SWT.TOP, false, false); layoutData.horizontalSpan = 2; layoutData.widthHint = 600; // required for wrapping restartNotice.setLayoutData(layoutData); // left column leftColumnComp = toolkit.createComposite(columnComp); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 10; layout.horizontalSpacing = 0; leftColumnComp.setLayout(layout); leftColumnComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL)); // right column rightColumnComp = toolkit.createComposite(columnComp); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 10; layout.horizontalSpacing = 0; rightColumnComp.setLayout(layout); rightColumnComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL)); refreshAction = new Action("Refresh", SWT.None) { @Override public ImageDescriptor getImageDescriptor() { return Ec2Plugin.getDefault().getImageRegistry().getDescriptor("refresh"); } @Override public void run() { refresh(null); } }; managedForm.getForm().getToolBarManager().add(refreshAction); exportTemplateAction = new Action("Export current values as template", SWT.None) { @Override public ImageDescriptor getImageDescriptor() { return ImageDescriptor.createFromFile(ElasticBeanstalkPlugin.class, "/icons/export.gif"); } @Override public void run() { exportAsTemplate(); } }; importTemplateAction = new Action("Import template values into editor", SWT.None) { @Override public ImageDescriptor getImageDescriptor() { return ImageDescriptor.createFromFile(ElasticBeanstalkPlugin.class, "/icons/import.gif"); } @Override public void run() { importTemplate(); } }; managedForm.getForm().getToolBarManager().add(importTemplateAction); managedForm.getForm().getToolBarManager().add(exportTemplateAction); managedForm.getForm().getToolBarManager().update(true); managedForm.reflow(true); refresh(null); } /** * Imports a template's config settings into the editor. */ protected void importTemplate() { AWSElasticBeanstalk client = AwsToolkitCore.getClientFactory(environment.getAccountId()) .getElasticBeanstalkClientByEndpoint(environment.getRegionEndpoint()); DescribeApplicationsResult result = client.describeApplications(new DescribeApplicationsRequest() .withApplicationNames(environment.getApplicationName())); final Collection<String> existingTemplateNames = new HashSet<String>(); for ( ApplicationDescription app : result.getApplications() ) { for ( String templateName : app.getConfigurationTemplates() ) { existingTemplateNames.add(templateName); } } final ImportTemplateDialog dialog = new ImportTemplateDialog(getSite().getShell(), existingTemplateNames); dialog.open(); if ( dialog.getReturnCode() == MessageDialog.OK ) { refresh(dialog.getTemplateName()); } } /** * Refreshes the editor with the latest values */ public void refresh(String templateName) { model.refresh(templateName); } /** * Exports the current model as a template, prompting the user for a name. */ private void exportAsTemplate() { AWSElasticBeanstalk client = AwsToolkitCore.getClientFactory(environment.getAccountId()) .getElasticBeanstalkClientByEndpoint(environment.getRegionEndpoint()); DescribeApplicationsResult result = client.describeApplications(new DescribeApplicationsRequest() .withApplicationNames(environment.getApplicationName())); final Collection<String> existingTemplateNames = new HashSet<String>(); for ( ApplicationDescription app : result.getApplications() ) { for ( String templateName : app.getConfigurationTemplates() ) { existingTemplateNames.add(templateName); } } final ExportTemplateDialog dialog = new ExportTemplateDialog(getSite().getShell(), existingTemplateNames, "newTemplate" + System.currentTimeMillis()); dialog.open(); if ( dialog.getReturnCode() == MessageDialog.OK ) { new ExportConfigurationJob(environment, dialog.getTemplateName(), dialog.getTemplateDescription(), model.createConfigurationOptions(), dialog.isCreatingNew()).schedule(); } } /** * Creates and returns the appropriate sections for the configuration * options given, one per namespace. */ private List<EnvironmentConfigEditorSection> createEditorSections(List<ConfigurationOptionDescription> options) { List<EnvironmentConfigEditorSection> editorSections = new ArrayList<EnvironmentConfigEditorSection>(); Map<String, List<ConfigurationOptionDescription>> optionsByNamespace = new HashMap<String, List<ConfigurationOptionDescription>>(); for ( ConfigurationOptionDescription o : options ) { if ( !optionsByNamespace.containsKey(o.getNamespace()) ) { ArrayList<ConfigurationOptionDescription> optionsInNamespace = new ArrayList<ConfigurationOptionDescription>(); optionsByNamespace.put(o.getNamespace(), optionsInNamespace); // We use our customized environment type section if (o.getNamespace().equals(ConfigurationOptionConstants.ENVIRONMENT_TYPE) && o.getName().equals("EnvironmentType")) { editorSections.add(new AdvancedEnvironmentTypeConfigEditorSection(this, model, environment, bindingContext, o.getNamespace(), optionsInNamespace)); } else { editorSections.add(new EnvironmentConfigEditorSection(this, model, environment, bindingContext, o.getNamespace(), optionsInNamespace)); } } optionsByNamespace.get(o.getNamespace()).add(o); } return editorSections; } public void refreshStarted() { /* * Although we are likely already in the UI thread, not executing this * in the context of the parent shell's UI thread leads to race * conditions with the RefreshThread's own composite manipulation. */ getEditorSite().getShell().getDisplay().syncExec(new Runnable() { public void run() { exportTemplateAction.setEnabled(false); importTemplateAction.setEnabled(false); managedForm.getForm().setText(getTitle() + " (loading...)"); } }); } public void refreshFinished() { getEditorSite().getShell().getDisplay().syncExec(new Runnable() { public void run() { // Every time we redraw the layouts. destroyOldLayouts(); final List<EnvironmentConfigEditorSection> editorSections = createEditorSections(model.getOptions()); int numLeft = 0; int numRight = 0; for (EnvironmentConfigEditorSection section : editorSections) { section.setServerEditorPart(EnvironmentConfigEditorPart.this); section.init(getEditorSite(), getEditorInput()); if (numLeft <= numRight) { section.createSection(leftColumnComp); numLeft += section.getNumControls(); } else { section.createSection(rightColumnComp); numRight += section.getNumControls(); } managedForm.reflow(true); } managedForm.getForm().setText(getTitle()); exportTemplateAction.setEnabled(true); importTemplateAction.setEnabled(true); } }); } public void refreshError(Throwable e) { ElasticBeanstalkPlugin.getDefault().getLog().log(new Status(Status.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, "Error creating editor", e)); } /** * Called on every editor part by the parent multi-page editor when the user * saves. An OK status means a save can take place; an error results in a * pop-up error dialog. */ @Override public IStatus[] getSaveStatus() { if ( !isDirty() ) return new IStatus[] { Status.OK_STATUS }; // Don't allow a save unless the form has no errors Object aggregateFormStatus = aggregateValidationStatus.getValue(); if ( aggregateFormStatus instanceof IStatus == false ) return new IStatus[] { Status.OK_STATUS }; else if ( ((IStatus) aggregateFormStatus).getSeverity() == IStatus.ERROR ) { return new IStatus[] { (IStatus) aggregateFormStatus }; } try { AWSElasticBeanstalk client = AwsToolkitCore.getClientFactory(environment.getAccountId()) .getElasticBeanstalkClientByEndpoint(environment.getRegionEndpoint()); ValidateConfigurationSettingsResult validation = client .validateConfigurationSettings(new ValidateConfigurationSettingsRequest() .withApplicationName(environment.getApplicationName()) .withEnvironmentName(environment.getEnvironmentName()) .withOptionSettings(model.createConfigurationOptions())); for ( ValidationMessage status : validation.getMessages() ) { if ( status.getSeverity().toLowerCase().equals("error") ) { return new IStatus[] { new Status(IStatus.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, status.getMessage()) }; } } // We can't save these values unless the environment is available List<EnvironmentDescription> envs = client.describeEnvironments( new DescribeEnvironmentsRequest().withEnvironmentNames(environment.getEnvironmentName())) .getEnvironments(); if ( envs.size() > 1 ) { return new IStatus[] { new Status(IStatus.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, "Environment busy") }; } if ( !envs.get(0).getStatus().equals(EnvironmentStatus.Ready.toString()) ) { return new IStatus[] { new Status(IStatus.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, "Environment must be available before saving") }; } } catch ( Exception e ) { return new IStatus[] { new Status(IStatus.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, e.getMessage(), e) }; } return new IStatus[] { Status.OK_STATUS }; } /** * Performs the save operation, implemented as a job. */ @Override public void doSave(IProgressMonitor monitor) { if (dirty) { UpdateEnvironmentRequest rq = new UpdateEnvironmentRequest(); rq.setEnvironmentName(environment.getEnvironmentName()); Collection<ConfigurationOptionSetting> settings = model.createConfigurationOptions(); rq.setOptionSettings(settings); UpdateEnvironmentConfigurationJob job = new UpdateEnvironmentConfigurationJob(environment, rq); job.schedule(); dirty = false; } } @Override public void destroyOldLayouts() { // Not allow refresh action during the destroying of controls refreshAction.setEnabled(false); if (leftColumnComp != null) { for (Control control : leftColumnComp.getChildren()) { control.dispose(); } } if (rightColumnComp != null) { for (Control control : rightColumnComp.getChildren()) { control.dispose(); } } refreshAction.setEnabled(true); } }