Package org.springframework.ide.eclipse.wizard.gettingstarted.boot

Source Code of org.springframework.ide.eclipse.wizard.gettingstarted.boot.NewSpringBootWizardModel

/*******************************************************************************
* Copyright (c) 2013 GoPivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are 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:
*     GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.wizard.gettingstarted.boot;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PlatformUI;
import org.springframework.ide.eclipse.wizard.WizardPlugin;
import org.springframework.ide.eclipse.wizard.gettingstarted.boot.json.InitializrServiceSpec;
import org.springframework.ide.eclipse.wizard.gettingstarted.boot.json.InitializrServiceSpec.DependencyGroup;
import org.springframework.ide.eclipse.wizard.gettingstarted.boot.json.InitializrServiceSpec.Depependency;
import org.springframework.ide.eclipse.wizard.gettingstarted.boot.json.InitializrServiceSpec.Option;
import org.springframework.ide.eclipse.wizard.gettingstarted.boot.json.InitializrServiceSpec.Type;
import org.springframework.ide.eclipse.wizard.gettingstarted.content.BuildType;
import org.springframework.ide.eclipse.wizard.gettingstarted.content.CodeSet;
import org.springframework.ide.eclipse.wizard.gettingstarted.importing.ImportStrategy;
import org.springframework.ide.eclipse.wizard.gettingstarted.importing.ImportUtils;
import org.springsource.ide.eclipse.commons.core.preferences.StsProperties;
import org.springsource.ide.eclipse.commons.frameworks.core.downloadmanager.DownloadManager;
import org.springsource.ide.eclipse.commons.frameworks.core.downloadmanager.DownloadableItem;
import org.springsource.ide.eclipse.commons.frameworks.core.downloadmanager.URLConnectionFactory;
import org.springsource.ide.eclipse.commons.livexp.core.FieldModel;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.core.StringFieldModel;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.Validator;
import org.springsource.ide.eclipse.commons.livexp.core.ValueListener;
import org.springsource.ide.eclipse.commons.livexp.core.validators.NewProjectLocationValidator;
import org.springsource.ide.eclipse.commons.livexp.core.validators.NewProjectNameValidator;
import org.springsource.ide.eclipse.commons.livexp.core.validators.UrlValidator;
import org.springsource.ide.eclipse.commons.livexp.ui.ProjectLocationSection;

/**
* A ZipUrlImportWizard is a simple wizard in which one can paste a url
* pointing to a zip file. The zip file is supposed to contain a maven (or gradle)
* project in the root of the zip.
*/
public class NewSpringBootWizardModel {

  private static final Map<String,BuildType> KNOWN_TYPES = new HashMap<String, BuildType>();
  static {
    KNOWN_TYPES.put("gradle-project", BuildType.GRADLE); // New version of initialzr app
    KNOWN_TYPES.put("maven-project", BuildType.MAVEN); // New versions of initialzr app

    KNOWN_TYPES.put("gradle.zip", BuildType.GRADLE); //Legacy, can remove when new initializr app uses "gradle-project" definitively
    KNOWN_TYPES.put("starter.zip", BuildType.MAVEN); //Legacy, can remove when initializr app uses "maven-project" definitively
  }

  /**
   * Lists known query parameters that map onto a String input field. The default values for these
   * parameters will be pulled from the json spec document.
   */
  private static final Map<String,String> KNOWN_STRING_INPUTS = new HashMap<String, String>();
  static {
    KNOWN_STRING_INPUTS.put("name", "Name");
    KNOWN_STRING_INPUTS.put("groupId", "Group");
    KNOWN_STRING_INPUTS.put("artifactId", "Artifact");
    KNOWN_STRING_INPUTS.put("version", "Version");
    KNOWN_STRING_INPUTS.put("description", "Description");
    KNOWN_STRING_INPUTS.put("packgageName", "Package");
  };

  private final URLConnectionFactory urlConnectionFactory;
  private final String JSON_URL;
  private final String CONTENT_TYPE;

  public NewSpringBootWizardModel() throws Exception {
    this(new URLConnectionFactory(), StsProperties.getInstance(new NullProgressMonitor()));
  }

  public NewSpringBootWizardModel(URLConnectionFactory urlConnectionFactory, StsProperties stsProps) throws Exception {
    this(urlConnectionFactory, stsProps.get("spring.initializr.json.url"), stsProps.get("spring.initializr.json.contentType"));
  }

  public NewSpringBootWizardModel(URLConnectionFactory urlConnectionFactory, String jsonUrl, String contentType) throws Exception {
    this.urlConnectionFactory = urlConnectionFactory;
    this.JSON_URL = jsonUrl;
    this.CONTENT_TYPE = contentType;

    baseUrl = new LiveVariable<String>("<computed>");
    baseUrlValidator = new UrlValidator("Base Url", baseUrl);

    discoverOptions(stringInputs, style);
    style.sort();

    projectName = stringInputs.getField("name");
    projectName.validator(new NewProjectNameValidator(projectName.getVariable()));
    location = new LiveVariable<String>(ProjectLocationSection.getDefaultProjectLocation(projectName.getValue()));
    locationValidator = new NewProjectLocationValidator("Location", location, projectName.getVariable());
    Assert.isNotNull(projectName, "The service at "+JSON_URL+" doesn't specify a 'name' text input");

    UrlMaker computedUrl = new UrlMaker(baseUrl);
    for (FieldModel<String> param : stringInputs) {
      computedUrl.addField(param);
    }
    computedUrl.addField(style);
    for (RadioGroup group : radioGroups.getGroups()) {
      computedUrl.addField(group);
    }
    computedUrl.addListener(new ValueListener<String>() {
      public void gotValue(LiveExpression<String> exp, String value) {
        downloadUrl.setValue(value);
      }
    });

    addBuildTypeValidator();
  }

  /**
   * If this wizard has a 'type' radioGroup to select the build type then add a validator to check if the
   * build type is supported.
   */
  private void addBuildTypeValidator() {
    RadioGroup buildTypeGroup = getRadioGroups().getGroup("type");
    if (buildTypeGroup!=null) {
      buildTypeGroup.validator(new Validator() {
        @Override
        protected ValidationResult compute() {
          BuildType bt = getBuildType();
          if (!bt.getImportStrategy().isSupported()) {
            //This means some required STS component like m2e or gradle tooling is not installed
            return ValidationResult.error(bt.getNotInstalledMessage());
          }
          return ValidationResult.OK;
        }
      }.dependsOn(buildTypeGroup.getVariable()));
    }
  }

  @SuppressWarnings("unchecked")
  public final FieldArrayModel<String> stringInputs = new FieldArrayModel<String>(
      //The fields need to be discovered by parsing web form.
  );

  public final MultiSelectionFieldModel<String> style = new MultiSelectionFieldModel<String>(String.class, "style")
      .label("Dependencies");

  private final FieldModel<String> projectName; //an alias for stringFields.getField("name");
  private final LiveVariable<String> location;
  private final NewProjectLocationValidator locationValidator;

  private boolean allowUIThread = false;

  public final LiveVariable<String> baseUrl;
  public final LiveExpression<ValidationResult> baseUrlValidator;

  public final LiveVariable<String> downloadUrl = new LiveVariable<String>();
  private IWorkingSet[] workingSets = new IWorkingSet[0];
  private RadioGroups radioGroups = new RadioGroups();

  public void performFinish(IProgressMonitor mon) throws InvocationTargetException, InterruptedException {
    mon.beginTask("Importing "+baseUrl.getValue(), 4);
    DownloadManager downloader = null;
    try {
      downloader = new DownloadManager().allowUIThread(allowUIThread);

      DownloadableItem zip = new DownloadableItem(newURL(downloadUrl .getValue()), downloader);
      String projectNameValue = projectName.getValue();
      CodeSet cs = CodeSet.fromZip(projectNameValue, zip, new Path("/"));

      IRunnableWithProgress oper = getImportStrategy().createOperation(ImportUtils.importConfig(
          new Path(location.getValue()),
          projectNameValue,
          cs
      ));
      oper.run(new SubProgressMonitor(mon, 3));

      IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectNameValue);
      addToWorkingSets(project, new SubProgressMonitor(mon, 1));

    } catch (IOException e) {
      throw new InvocationTargetException(e);
    } finally {
      if (downloader!=null) {
        downloader.dispose();
      }
      mon.done();
    }
  }

  /**
   * Get currently selected import strategy.
   * Never returns null (some default is returned in any case).
   */
  public ImportStrategy getImportStrategy() {
    return getBuildType().getImportStrategy();
  }

  /**
   * Gets the currently selected BuildType.
   * Never returns null (some default is returned in any case).
   */
  public BuildType getBuildType() {
    RadioGroup buildTypeRadios = getRadioGroups().getGroup("type");
    if (buildTypeRadios!=null) {
      RadioInfo selected = buildTypeRadios.getSelection().selection.getValue();
      if (selected!=null) {
        BuildType bt = KNOWN_TYPES.get(selected.getValue());
        if (bt!=null) {
          return bt;
        } else {
          //Uknown build type, import it as a general project which is better than nothing
          return BuildType.GENERAL;
        }
      }
    }
    //Old initialzr app doesn't have button to specify build type... it is always maven
    return BuildType.MAVEN;
  }

  private void addToWorkingSets(IProject project, IProgressMonitor monitor) {
    monitor.beginTask("Add '"+project.getName()+"' to working sets", 1);
    try {
      if (workingSets==null || workingSets.length==0) {
        return;
      }
      IWorkingSetManager wsm = PlatformUI.getWorkbench().getWorkingSetManager();
      wsm.addToWorkingSets(project, workingSets);
    } finally {
      monitor.done();
    }
  }

  /**
   * Dynamically discover input fields and 'style' options by parsing initializr form.
   */
  private void discoverOptions(FieldArrayModel<String> fields, MultiSelectionFieldModel<String> style) throws Exception {
    InitializrServiceSpec serviceSpec = parseJsonFrom(new URL(JSON_URL));

    for (Entry<String, String> e : KNOWN_STRING_INPUTS.entrySet()) {
      String name = e.getKey();
      String defaultValue = serviceSpec.getDefaults().get(name);
      if (defaultValue!=null) {
        fields.add(new StringFieldModel(name, defaultValue).label(e.getValue()));
      }
    }

    {  //field: type
      String groupName = "type";
      RadioGroup group = radioGroups.ensureGroup(groupName);
      group.label("Type:");
      for (Type type : serviceSpec.getTypes()) {
        if (KNOWN_TYPES.containsKey(type.getId())) {
          TypeRadioInfo radio = new TypeRadioInfo(groupName, type.getId(), type.isDefault(), type.getAction());
          radio.setLabel(type.getName());
          group.add(radio);
        }
      }
      //When a type is selected the 'baseUrl' should be update according to its action.
      group.getSelection().selection.addListener(new ValueListener<RadioInfo>() {
        public void gotValue(LiveExpression<RadioInfo> exp, RadioInfo value) {
          try {
            if (value!=null) {
              URI base = new URI(JSON_URL);
              URI resolved = base.resolve(((TypeRadioInfo)value).getAction());
              baseUrl.setValue(resolved.toString());
            }
          } catch (Exception e) {
            WizardPlugin.log(e);
          }
        }
      });
    }

    {  //field: packaging
      String groupName = "packaging";
      RadioGroup group = radioGroups.ensureGroup(groupName);
      group.label("Packaging:");
      addOptions(group, serviceSpec.getPackagings());
    }

    {  //field: javaVersion
      String groupName = "javaVersion";
      RadioGroup group = radioGroups.ensureGroup(groupName);
      group.label("Java Version:");
      addOptions(group, serviceSpec.getJavaVersions());
    }

    {  //field: language
      String groupName = "language";
      RadioGroup group = radioGroups.ensureGroup(groupName);
      group.label("Language:");
      addOptions(group, serviceSpec.getLanguages());
    }

    {  //field: bootVersion
      String groupName = "bootVersion";
      RadioGroup group = radioGroups.ensureGroup(groupName);
      group.label("Boot Version:");
      addOptions(group, serviceSpec.getBootVersions());
    }

    //styles
    for (DependencyGroup dgroup : serviceSpec.getDependencies()) {
      for (Depependency dep : dgroup.getContent()) {
        style.choice(dep.getName(), dep.getId(), dep.getDescription());
      }
    }
  }

  private void addOptions(RadioGroup group, Option[] options) {
    for (Option option : options) {
      RadioInfo radio = new RadioInfo(group.getName(), option.getId(), option.isDefault());
      radio.setLabel(option.getName());
      group.add(radio);
    }
  }

  private InitializrServiceSpec parseJsonFrom(URL url) throws Exception {
    URLConnection conn = null;
    InputStream input = null;
    try {
      conn = urlConnectionFactory.createConnection(url);
      if (CONTENT_TYPE!=null) {
        conn.addRequestProperty("Accept", CONTENT_TYPE);
      }
      conn.connect();
      input = conn.getInputStream();
      return InitializrServiceSpec.parseFrom(input);
    } finally {
      if (input!=null) {
        try {
          input.close();
        } catch (IOException e) {
        }
      }
    }
  }

  private URL newURL(String value) {
    try {
      return new URL(value);
    } catch (MalformedURLException e) {
      //This should be impossible because the URL syntax is validated beforehand.
      WizardPlugin.log(e);
      return null;
    }
  }

  /**
   * This is mostly for testing purposes where it is just easier to run stuff in the UIThread (test do so
   * by default). But in production we shouldn't allow downloading stuff in the UIThread.
   */
  public void allowUIThread(boolean allow) {
    this.allowUIThread = allow;
  }

  public LiveExpression<ValidationResult> getLocationValidator() {
    return locationValidator;
  }

  public LiveVariable<String> getLocation() {
    return location;
  }

  public FieldModel<String> getProjectName() {
    return projectName;
  }

  public void setWorkingSets(IWorkingSet[] workingSets) {
    this.workingSets = workingSets;
  }

  public RadioGroups getRadioGroups() {
    return this.radioGroups;
  }

}
TOP

Related Classes of org.springframework.ide.eclipse.wizard.gettingstarted.boot.NewSpringBootWizardModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.