/* * 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.basic; 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.Status; import org.eclipse.jface.action.Action; 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.ExpandableComposite; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; import org.eclipse.wst.server.ui.internal.ImageResource; import com.amazonaws.eclipse.ec2.Ec2Plugin; import com.amazonaws.eclipse.elasticbeanstalk.ConfigurationOptionConstants; import com.amazonaws.eclipse.elasticbeanstalk.ElasticBeanstalkPlugin; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.configEditor.AbstractEnvironmentConfigEditorPart; import com.amazonaws.eclipse.elasticbeanstalk.server.ui.configEditor.RefreshListener; import com.amazonaws.services.elasticbeanstalk.model.ConfigurationOptionDescription; /** * Basic environment configuration editor with reduced options and * human-readable names. */ @SuppressWarnings({ "unchecked", "restriction" }) public class BasicEnvironmentConfigEditorPart extends AbstractEnvironmentConfigEditorPart implements RefreshListener { /* * Map of which namespace controls need to be grouped into which editor. */ private final Map<Collection<String>, HumanReadableConfigEditorSection> editorSectionsByNamespace = new HashMap<Collection<String>, HumanReadableConfigEditorSection>(); private static final Collection<String> serverNamespaces = new HashSet<String>(); private static final Collection<String> loadBalancingNamespaces = new HashSet<String>(); private static final Collection<String> healthCheckNamespaces = new HashSet<String>(); private static final Collection<String> sessionsNamespaces = new HashSet<String>(); private static final Collection<String> autoscalingNamespaces = new HashSet<String>(); private static final Collection<String> triggerNamespaces = new HashSet<String>(); private static final Collection<String> notificationsNamespaces = new HashSet<String>(); private static final Collection<String> containerNamespaces = new HashSet<String>(); private static final Collection<String> applicationEnvironmentNamespaces = new HashSet<String>(); private static final Collection<String> environmentNamespaces = new HashSet<String>(); private static final Collection<String> rollingDeploymentsNamespaces = new HashSet<String>(); static { serverNamespaces.add(ConfigurationOptionConstants.LAUNCHCONFIGURATION); loadBalancingNamespaces.add(ConfigurationOptionConstants.LOADBALANCER); healthCheckNamespaces.add(ConfigurationOptionConstants.HEALTHCHECK); healthCheckNamespaces.add(ConfigurationOptionConstants.APPLICATION); sessionsNamespaces.add(ConfigurationOptionConstants.POLICIES); autoscalingNamespaces.add(ConfigurationOptionConstants.ASG); triggerNamespaces.add(ConfigurationOptionConstants.TRIGGER); notificationsNamespaces.add(ConfigurationOptionConstants.SNS_TOPICS); containerNamespaces.add(ConfigurationOptionConstants.JVMOPTIONS); containerNamespaces.add(ConfigurationOptionConstants.HOSTMANAGER); applicationEnvironmentNamespaces.add(ConfigurationOptionConstants.ENVIRONMENT); environmentNamespaces.add(ConfigurationOptionConstants.ENVIRONMENT_TYPE); rollingDeploymentsNamespaces.add(ConfigurationOptionConstants.COMMAND); } private static final Collection<NamespaceGroup> sectionGroups = new ArrayList<NamespaceGroup>(); static { sectionGroups.add(new NamespaceGroup("Server", Position.LEFT, serverNamespaces)); sectionGroups.add(new NamespaceGroup("Load Balancing", Position.LEFT, loadBalancingNamespaces, healthCheckNamespaces, sessionsNamespaces)); sectionGroups.add(new NamespaceGroup("Environment Type", Position.RIGHT, environmentNamespaces)); sectionGroups.add(new NamespaceGroup("Rolling Deployments", Position.RIGHT, rollingDeploymentsNamespaces)); sectionGroups.add(new NamespaceGroup("Auto Scaling", Position.RIGHT, autoscalingNamespaces, triggerNamespaces)); sectionGroups.add(new NamespaceGroup("Notifications", Position.RIGHT, notificationsNamespaces)); sectionGroups.add(new NamespaceGroup("Container", Position.CENTER, containerNamespaces, applicationEnvironmentNamespaces)); } private static final Collection<String>[] sectionOrder = new Collection[] { serverNamespaces, loadBalancingNamespaces,healthCheckNamespaces, sessionsNamespaces, environmentNamespaces, rollingDeploymentsNamespaces, autoscalingNamespaces, triggerNamespaces, notificationsNamespaces, containerNamespaces, applicationEnvironmentNamespaces, }; /** * Each time we create our control section, we create one composite for each * group of namespaces. */ private Map<String, Composite> compositesByNamespace; public BasicEnvironmentConfigEditorPart() { super(); } @Override public void init(IEditorSite site, IEditorInput input) { super.init(site, input); editorSectionsByNamespace.put(serverNamespaces, new ServerConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(loadBalancingNamespaces, new LoadBalancingConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(healthCheckNamespaces, new HealthCheckConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(sessionsNamespaces, new SessionConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(autoscalingNamespaces, new AutoScalingConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(triggerNamespaces, new ScalingTriggerConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(notificationsNamespaces, new NotificationsConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(containerNamespaces, new ContainerConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(applicationEnvironmentNamespaces, new EnvironmentPropertiesConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(environmentNamespaces, new EnvironmentTypeConfigEditorSection(this, model, environment, bindingContext)); editorSectionsByNamespace.put(rollingDeploymentsNamespaces, new RollingDeploymentsConfigEditorSection(this, model, environment, bindingContext)); 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 Composite 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 Composite 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)); // "center" column -- composite below the two above, spanning both columns Composite centerColumnComp = toolkit.createComposite(columnComp); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 10; layout.horizontalSpacing = 0; centerColumnComp.setLayout(layout); layoutData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL); layoutData.horizontalSpan = 2; centerColumnComp.setLayoutData(layoutData); compositesByNamespace = new HashMap<String, Composite>(); for ( NamespaceGroup namespaceGroup : sectionGroups ) { Composite parentComp = null; switch (namespaceGroup.getPosition()) { case LEFT: parentComp = leftColumnComp; break; case RIGHT: parentComp = rightColumnComp; break; case CENTER: parentComp = centerColumnComp; break; } Section section = toolkit.createSection(parentComp, ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR | ExpandableComposite.FOCUS_TITLE); section.setText(namespaceGroup.getName()); layout = new GridLayout(); layout.numColumns = 1; layout.verticalSpacing = 0; section.setLayout(layout); section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL)); Composite comp = toolkit.createComposite(section); layout = new GridLayout(); int numColumns = 0; switch (namespaceGroup.getPosition()) { case LEFT: case RIGHT: numColumns = 1; break; case CENTER: numColumns = 2; break; } layout.numColumns = numColumns; layout.verticalSpacing = 0; comp.setLayout(layout); comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL)); section.setClient(comp); for (String namespace : namespaceGroup.getIncludedNamespaces()) { compositesByNamespace.put(namespace, comp); } } 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); managedForm.getForm().getToolBarManager().update(true); managedForm.reflow(true); } /** * Refreshes the editor with the latest values */ public void refresh(String templateName) { model.refresh(templateName); } public void destroyOldLayouts() { // Not allow refresh action during the destroying of controls refreshAction.setEnabled(false); for (Composite composite : compositesByNamespace.values()) { for (Control control : composite.getChildren()) { control.dispose(); } } refreshAction.setEnabled(true); } private List<HumanReadableConfigEditorSection> createEditorSections(List<ConfigurationOptionDescription> options) { List<HumanReadableConfigEditorSection> editorSections = new ArrayList<HumanReadableConfigEditorSection>(); for ( Collection<String> namespaces : sectionOrder ) { ArrayList<ConfigurationOptionDescription> optionsInEditorSection = new ArrayList<ConfigurationOptionDescription>(); for ( ConfigurationOptionDescription o : options ) { if ( namespaces.contains(o.getNamespace()) && editorSectionsByNamespace.containsKey(namespaces) ) { if ( optionsInEditorSection.isEmpty() ) { HumanReadableConfigEditorSection editor = editorSectionsByNamespace.get(namespaces); editor.setOptions(optionsInEditorSection); editorSections.add(editor); editor.setParentComposite(compositesByNamespace.get(o.getNamespace())); } optionsInEditorSection.add(o); } } } return editorSections; } public void refreshStarted() { getEditorSite().getShell().getDisplay().syncExec(new Runnable() { public void run() { managedForm.getForm().setText(getTitle() + " (loading...)"); } }); } public void refreshFinished() { getEditorSite().getShell().getDisplay().syncExec(new Runnable() { public void run() { if ( compositesByNamespace.values().iterator().next().getChildren().length == 0 ) { final List<HumanReadableConfigEditorSection> editorSections = createEditorSections(model .getOptions()); for ( HumanReadableConfigEditorSection section : editorSections ) { // Skip creating "Auto Scaling" and "Rolling Deployments" sections if the // environment is deployed to single-instance if (section instanceof RollingDeploymentsConfigEditorSection || section instanceof AutoScalingConfigEditorSection) { Object envTypeValue = model.getEntry( new ConfigurationOptionDescription() .withNamespace(ConfigurationOptionConstants.ENVIRONMENT_TYPE) .withName("EnvironmentType")); if ("SingleInstance".equals(envTypeValue)) { continue; } } section.setServerEditorPart(BasicEnvironmentConfigEditorPart.this); section.init(getEditorSite(), getEditorInput()); section.createSection(section.getParentComposite()); managedForm.reflow(true); } } dirty = false; BasicEnvironmentConfigEditorPart.this.doSaveAs(); managedForm.getForm().setText(getTitle()); } }); } public void refreshError(Throwable e) { ElasticBeanstalkPlugin.getDefault().getLog().log(new Status(Status.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, "Error creating editor", e)); } private static enum Position { LEFT, RIGHT, CENTER }; /** * Simple data class to avoid too many levels of collection nesting. */ private static final class NamespaceGroup { private final Collection<String> includedNamespaces; final String name; final Position position; public NamespaceGroup(String name, Position pos, Collection<String>... namespaces) { this.name = name; includedNamespaces = new HashSet<String>(); for ( Collection<String> namespace : namespaces ) { includedNamespaces.addAll(namespace); } position = pos; } public String getName() { return name; } public Collection<String> getIncludedNamespaces() { return includedNamespaces; } public Position getPosition() { return position; } } }