summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Wulfe <benwu@google.com>2014-08-14 19:17:50 +0000
committerBenjamin Wulfe <benwu@google.com>2014-08-14 19:17:50 +0000
commita6fc4da6327ef1167a68c66fd94803d47b719f36 (patch)
treed4bae115d38cfa68fcf2939384f9f43b8056da66
parentc4e5891127fd79847100dd1ea890a37655f937b5 (diff)
downloadcloud-a6fc4da6327ef1167a68c66fd94803d47b719f36.tar.gz
Revert "Revert "Creates a simple deploy dialog using appcfg.""
Dependent changes for tools/idea now in master. This reverts commit c4e5891127fd79847100dd1ea890a37655f937b5. Change-Id: I7ac242ea1584dd859316a90c33994d5d78911f73
-rw-r--r--google-cloud-tools.iml1
-rw-r--r--login/src/com/google/gct/login/OAuthScopeRegistry.java1
-rw-r--r--src/META-INF/plugin.xml6
-rw-r--r--src/com/google/gct/idea/appengine/deploy/AppEngineUpdateAction.java33
-rw-r--r--src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.form75
-rw-r--r--src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java275
-rw-r--r--src/com/google/gct/idea/appengine/deploy/AppEngineUpdater.java218
-rw-r--r--src/com/google/gct/idea/appengine/dom/AppEngineWebApp.java27
-rw-r--r--src/com/google/gct/idea/appengine/dom/AppEngineWebFileDescription.java39
-rw-r--r--src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java34
-rw-r--r--src/com/google/gct/idea/appengine/initialization/CloudPluginRegistration.java64
11 files changed, 773 insertions, 0 deletions
diff --git a/google-cloud-tools.iml b/google-cloud-tools.iml
index e07f852..9126f4b 100644
--- a/google-cloud-tools.iml
+++ b/google-cloud-tools.iml
@@ -53,6 +53,7 @@
<SOURCES />
</library>
</orderEntry>
+ <orderEntry type="module" module-name="google-login" />
</component>
</module>
diff --git a/login/src/com/google/gct/login/OAuthScopeRegistry.java b/login/src/com/google/gct/login/OAuthScopeRegistry.java
index ca2fe2e..47b9686 100644
--- a/login/src/com/google/gct/login/OAuthScopeRegistry.java
+++ b/login/src/com/google/gct/login/OAuthScopeRegistry.java
@@ -32,6 +32,7 @@ class OAuthScopeRegistry {
static {
SortedSet<String> scopes = new TreeSet<String>();
scopes.add("https://www.googleapis.com/auth/userinfo#email");
+ scopes.add("https://www.googleapis.com/auth/appengine.admin");
sScopes = Collections.unmodifiableSortedSet(scopes);
}
diff --git a/src/META-INF/plugin.xml b/src/META-INF/plugin.xml
index c34f079..c6bc850 100644
--- a/src/META-INF/plugin.xml
+++ b/src/META-INF/plugin.xml
@@ -18,6 +18,9 @@
<implementation-class>com.google.gct.idea.appengine.synchronization.SampleSyncRegistration</implementation-class>
</component>
-->
+ <component>
+ <implementation-class>com.google.gct.idea.appengine.initialization.CloudPluginRegistration</implementation-class>
+ </component>
</application-components>
<project-components>
@@ -44,6 +47,9 @@
<runConfigurationProducer implementation="com.google.gct.idea.appengine.run.AppEngineRunConfigurationProducer"/>
-->
+ <!-- Dom for the App Engine config file -->
+ <dom.fileDescription implementation="com.google.gct.idea.appengine.dom.AppEngineWebFileDescription"/>
+
<implicitUsageProvider implementation="com.google.gct.idea.appengine.validation.EndpointImplicitUsageProvider"/>
<localInspection language="JAVA" shortName="ApiName" bundle="messages.EndpointBundle" hasStaticDescription="true"
diff --git a/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateAction.java b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateAction.java
new file mode 100644
index 0000000..b50389d
--- /dev/null
+++ b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateAction.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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.google.gct.idea.appengine.deploy;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.module.Module;
+
+/**
+ * Handles the menu action to deploy to AppEngine.
+ */
+public class AppEngineUpdateAction extends AnAction {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Module selectedModule = LangDataKeys.MODULE.getData(e.getDataContext());
+
+ AppEngineUpdateDialog.show(e.getProject(), selectedModule);
+ }
+}
diff --git a/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.form b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.form
new file mode 100644
index 0000000..249d6a6
--- /dev/null
+++ b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.form
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.google.gct.idea.appengine.deploy.AppEngineUpdateDialog">
+ <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="2" vgap="2">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="306" height="113"/>
+ </constraints>
+ <properties>
+ <preferredSize width="275" height="135"/>
+ </properties>
+ <border type="none"/>
+ <children>
+ <component id="c6667" class="com.intellij.ui.components.JBLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Module:"/>
+ </properties>
+ </component>
+ <vspacer id="8a93e">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <component id="9f4da" class="com.intellij.openapi.roots.ui.configuration.ModulesCombobox" binding="myModuleComboBox">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="b23a6" class="com.intellij.ui.components.JBLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Project ID:"/>
+ </properties>
+ </component>
+ <component id="c6f41" class="javax.swing.JTextField" binding="myProjectId">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <editable value="true"/>
+ <enabled value="true"/>
+ <horizontalAlignment value="2"/>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="1011a" class="com.intellij.ui.components.JBLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Version:"/>
+ </properties>
+ </component>
+ <component id="331e4" class="javax.swing.JTextField" binding="myVersion">
+ <constraints>
+ <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <editable value="true"/>
+ <enabled value="true"/>
+ <horizontalAlignment value="2"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java
new file mode 100644
index 0000000..bd8c2f1
--- /dev/null
+++ b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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.google.gct.idea.appengine.deploy;
+
+import com.google.common.base.Strings;
+import com.google.gct.idea.appengine.dom.AppEngineWebApp;
+import com.google.gct.idea.appengine.dom.AppEngineWebFileDescription;
+import com.google.gct.idea.appengine.gradle.facet.AppEngineConfigurationProperties;
+import com.google.gct.idea.appengine.gradle.facet.AppEngineGradleFacet;
+import com.google.gct.login.GoogleLogin;
+import com.google.gct.login.IGoogleLoginCompletedCallback;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesCombobox;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.ui.SortedComboBoxModel;
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.DomFileElement;
+import com.intellij.util.xml.DomManager;
+import com.intellij.xml.util.XmlStringUtil;
+import org.eclipse.jgit.util.StringUtils;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * AppEngineUpdateDialog shows a dialog allowing the user to select a module and deploy.
+ */
+public class AppEngineUpdateDialog extends DialogWrapper {
+ private static final Logger LOG = Logger.getInstance(AppEngineUpdateDialog.class);
+
+ private ModulesCombobox myModuleComboBox;
+ private JTextField myProjectId;
+ private JTextField myVersion;
+ private JPanel myPanel;
+ private List<Module> myDeployableModules;
+ private Project myProject;
+ private Module myInitiallySelectedModule;
+
+ private AppEngineUpdateDialog(Project project, List<Module> deployableModules, Module selectedModule) {
+ super(project, true);
+ myDeployableModules = deployableModules;
+ myProject = project;
+ myInitiallySelectedModule = selectedModule;
+
+ init();
+ initValidation();
+ setTitle("Deploy to App Engine");
+ setOKButtonText("Deploy");
+
+ Window myWindow = getWindow();
+ if (myWindow != null) {
+ myWindow.setPreferredSize(new Dimension(285, 135));
+ }
+ }
+
+ /**
+ * Shows a dialog to deploy a module to AppEngine. Will force a login if required
+ * If either the login fails or there are no valid modules to upload, it will return after
+ * displaying an error.
+ *
+ * @param project The project whose modules will be uploaded.
+ * @param selectedModule The module selected by default in the deploy dialog. Can be null. If null or not a valid app engine module,
+ * no module will be selected by default.
+ */
+ static void show(final Project project, Module selectedModule) {
+
+ final java.util.List<Module> modules = new ArrayList<Module>();
+
+ // Filter the module list by whether we can actually deploy them to appengine.
+ for (Module module : ModuleManager.getInstance(project).getModules()) {
+ AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(module);
+ if (facet != null) {
+ modules.add(module);
+ }
+ }
+
+ // Tell the user what he has to do if he has none.
+ if (modules.size() == 0) {
+ //there are no modules to upload -- or we hit a bug due to gradle sync.
+ //TODO do we need to use the mainwindow as owner?
+ Messages.showErrorDialog(
+ XmlStringUtil.wrapInHtml(
+ "This project does not contain any App Engine modules. To add an App Engine module for your project, <br> open “File > New Module…” menu and choose one of App Engine modules.")
+ , "Error");
+ return;
+ }
+
+ if (selectedModule != null && !modules.contains(selectedModule)) {
+ selectedModule = null;
+ }
+
+ if (selectedModule == null && modules.size() == 1) {
+ selectedModule = modules.get(0);
+ }
+
+ // To invoke later, we need a final local.
+ final Module passedSelectedModule = selectedModule;
+
+ // Login on demand and queue up the dialog to show after a successful login.
+ //if login fails, it already shows an error.
+ if (!GoogleLogin.getInstance().isLoggedIn()) {
+ // log in on demand...
+ GoogleLogin.getInstance().logIn(null, new IGoogleLoginCompletedCallback() {
+ @Override
+ public void onLoginCompleted() {
+ if (GoogleLogin.getInstance().isLoggedIn()) {
+ EventQueue.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ // Success!, lets run the deploy now.
+ AppEngineUpdateDialog dialog = new AppEngineUpdateDialog(project, modules, passedSelectedModule);
+ dialog.show();
+ }
+ });
+ }
+ }
+ });
+ }
+ else {
+ AppEngineUpdateDialog dialog = new AppEngineUpdateDialog(project, modules, passedSelectedModule);
+ dialog.show();
+ }
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ @SuppressWarnings("unchecked")
+ final SortedComboBoxModel<Module> model = (SortedComboBoxModel<Module>)myModuleComboBox.getModel();
+ model.clear();
+ model.addAll(myDeployableModules);
+
+ if (myInitiallySelectedModule != null) {
+ // Auto select if there is only one item
+ model.setSelectedItem(myInitiallySelectedModule);
+ populateFields();
+ }
+
+ myModuleComboBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ populateFields();
+ }
+ });
+ return myPanel;
+ }
+
+ private void populateFields() {
+ myProjectId.setText("");
+ myVersion.setText("");
+
+ Module appEngineModule = myModuleComboBox.getSelectedModule();
+ if (appEngineModule != null) {
+ AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(appEngineModule);
+ if (facet == null) {
+ Messages.showErrorDialog(this.getPeer().getOwner(), "Could not acquire App Engine module information.", "Deploy");
+ return;
+ }
+
+ final AppEngineWebApp appEngineWebApp = facet.getAppEngineWebXml();
+ if (appEngineWebApp == null) {
+ Messages.showErrorDialog(this.getPeer().getOwner(), "Could not locate or parse the appengine-web.xml fle.", "Deploy");
+ return;
+ }
+
+ myProjectId.setText(appEngineWebApp.getApplication().getRawText());
+ myVersion.setText(appEngineWebApp.getVersion().getRawText());
+ }
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (getOKAction().isEnabled()) {
+ GoogleLogin login = GoogleLogin.getInstance();
+ Module selectedModule = myModuleComboBox.getSelectedModule();
+ String sdk = "";
+ String war = "";
+ AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(selectedModule);
+ if (facet != null) {
+ AppEngineConfigurationProperties model = facet.getConfiguration().getState();
+ sdk = model.APPENGINE_SDKROOT;
+ war = model.WAR_DIR;
+ }
+
+ String client_secret = login.fetchOAuth2ClientSecret();
+ String client_id = login.fetchOAuth2ClientId();
+ String refresh_token = login.fetchOAuth2RefreshToken();
+
+ if (StringUtils.isEmptyOrNull(client_secret) ||
+ StringUtils.isEmptyOrNull(client_id) ||
+ StringUtils.isEmptyOrNull(refresh_token)) {
+ // The login is somehow invalid, bail -- this shouldn't happen.
+ LOG.error("StartUploading while logged in, but it doesn't have full credentials.");
+ Messages.showErrorDialog(this.getPeer().getOwner(), "Login credentials are not valid.", "Login");
+ return;
+ }
+
+ // These should not fail as they are a part of the dialog validation.
+ if (Strings.isNullOrEmpty(sdk) ||
+ Strings.isNullOrEmpty(war) ||
+ Strings.isNullOrEmpty(myProjectId.getText()) ||
+ selectedModule == null) {
+ Messages.showErrorDialog(this.getPeer().getOwner(), "Could not deploy due to missing information (sdk/war/projectid).", "Deploy");
+ LOG.error("StartUploading was called with bad module/sdk/war");
+ return;
+ }
+
+ close(OK_EXIT_CODE); // We close before kicking off the update so it doesn't interfere with the output window coming to focus.
+
+ // Kick off the upload. detailed status will be shown in an output window.
+ new AppEngineUpdater(myProject, selectedModule, sdk, war, myProjectId.getText(), myVersion.getText(),
+ client_secret, client_id, refresh_token).startUploading();
+ }
+ }
+
+ @Override
+ protected ValidationInfo doValidate() {
+ // These should not normally occur..
+ if (!GoogleLogin.getInstance().isLoggedIn()) {
+ return new ValidationInfo("You must be logged in to perform this action.");
+ }
+
+ Module module = myModuleComboBox.getSelectedModule();
+ if (module == null) {
+ return new ValidationInfo("Select a module");
+ }
+
+ AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(module);
+ if (facet == null) {
+ return new ValidationInfo("Could not find App Engine gradle configuration on Module");
+ }
+
+ // We'll let AppCfg error if the project is wrong. The user can see this in the console window.
+ // Note that version can be blank to indicate current version.
+ if (Strings.isNullOrEmpty(myProjectId.getText())) {
+ return new ValidationInfo("Please enter a Project ID.");
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/com/google/gct/idea/appengine/deploy/AppEngineUpdater.java b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdater.java
new file mode 100644
index 0000000..aa48dd4
--- /dev/null
+++ b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdater.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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.google.gct.idea.appengine.deploy;
+
+import com.android.tools.idea.gradle.invoker.GradleInvoker;
+import com.google.common.base.Strings;
+import com.google.gct.idea.appengine.sdk.AppEngineSdk;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionManager;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.CommandLineBuilder;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.execution.configurations.JavaParameters;
+import com.intellij.execution.configurations.ParametersList;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.execution.filters.TextConsoleBuilderFactory;
+import com.intellij.execution.process.*;
+import com.intellij.execution.ui.ConsoleView;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.execution.ui.RunnerLayoutUi;
+import com.intellij.execution.ui.actions.CloseAction;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPlaces;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.KeyValue;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.net.HttpConfigurable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+import java.util.List;
+
+/**
+ * Compiles and deploys a module to AppEngine using AppCfg.
+ *
+ * @author benwu
+ */
+class AppEngineUpdater {
+ private static final Logger LOG = Logger.getInstance("#com.google.gct.idea.appengine.deploy.AppEngineUpdater");
+ private final Project myProject;
+ private final Module myModule;
+ private final String myExplodedWarPath;
+ private final String mySdkPath;
+ private final String myClientSecret;
+ private final String myClientId;
+ private final String myRefreshToken;
+ private final String myVersion;
+ private final String myAppEngineProject;
+
+ AppEngineUpdater(Project project,
+ Module module,
+ String sdkPath,
+ String explodedWarPath,
+ String appEngineProject,
+ String version,
+ String clientSecret,
+ String clientId,
+ String refreshToken) {
+ myProject = project;
+ myModule = module;
+ mySdkPath = sdkPath;
+ myExplodedWarPath = explodedWarPath;
+ myClientSecret = clientSecret;
+ myClientId = clientId;
+ myRefreshToken = refreshToken;
+ myVersion = version;
+ myAppEngineProject = appEngineProject;
+ }
+
+ /**
+ * Starts the compile and upload async process.
+ */
+ void startUploading() {
+ FileDocumentManager.getInstance().saveAllDocuments();
+ ProgressManager.getInstance().run(new Task.Backgroundable(myModule.getProject(), "Deploying application", true, null) {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ compileAndUpload();
+ }
+ });
+ }
+
+ private void compileAndUpload() {
+ final Runnable startUploading = new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ startUploadingProcess();
+ }
+ });
+ }
+ };
+
+ GradleInvoker.getInstance(myProject).compileJava(new Module[]{myModule});
+ startUploading.run();
+ }
+
+ private void startUploadingProcess() {
+ final Process process;
+ final GeneralCommandLine commandLine;
+
+ try {
+ JavaParameters parameters = new JavaParameters();
+ parameters.configureByModule(myModule, JavaParameters.JDK_ONLY);
+ parameters.setMainClass("com.google.appengine.tools.admin.AppCfg");
+ AppEngineSdk mySdk = new AppEngineSdk(mySdkPath);
+ parameters.getClassPath().add(mySdk.getToolsApiJarFile().getAbsolutePath());
+
+ final List<KeyValue<String, String>> list = HttpConfigurable.getJvmPropertiesList(false, null);
+ if (!list.isEmpty()) {
+ final ParametersList parametersList = parameters.getVMParametersList();
+ for (KeyValue<String, String> value : list) {
+ parametersList.defineProperty(value.getKey(), value.getValue());
+ }
+ }
+
+ final ParametersList programParameters = parameters.getProgramParametersList();
+ programParameters.add("--application=" + myAppEngineProject);
+ if (!Strings.isNullOrEmpty(myVersion)) {
+ programParameters.add("--version=" + myVersion);
+ }
+ programParameters.add("--oauth2");
+ programParameters.add("--oauth2_client_secret=" + myClientSecret);
+ programParameters.add("--oauth2_client_id=" + myClientId);
+ programParameters.add("--oauth2_refresh_token=" + myRefreshToken);
+ programParameters.add("update");
+ programParameters.add(FileUtil.toSystemDependentName(myExplodedWarPath));
+
+ commandLine = CommandLineBuilder.createFromJavaParameters(parameters);
+
+ process = commandLine.createProcess();
+ }
+ catch (ExecutionException e) {
+ final String message = e.getMessage();
+ LOG.error("Cannot start uploading: " + message);
+
+ if (!EventQueue.isDispatchThread()) {
+ EventQueue.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ Messages.showErrorDialog("Cannot start uploading: " + message, "Error");
+ }
+ });
+ }
+ else {
+ Messages.showErrorDialog("Cannot start uploading: " + message, "Error");
+ }
+
+ return;
+ }
+
+ final ProcessHandler processHandler = new FilteredOSProcessHandler(process, commandLine.getCommandLineString(),
+ new String[]{myRefreshToken, myClientSecret, myClientId});
+ final Executor executor = DefaultRunExecutor.getRunExecutorInstance();
+ final ConsoleView console = TextConsoleBuilderFactory.getInstance().createBuilder(myModule.getProject()).getConsole();
+ final RunnerLayoutUi ui = RunnerLayoutUi.Factory.getInstance(myModule.getProject())
+ .create("Deploy", "Deploy to AppEngine", "Deploy Application", myModule.getProject());
+ final DefaultActionGroup group = new DefaultActionGroup();
+ ui.getOptions().setLeftToolbar(group, ActionPlaces.UNKNOWN);
+ ui.addContent(ui.createContent("upload", console.getComponent(), "Deploy Application", null, console.getPreferredFocusableComponent()));
+
+ console.attachToProcess(processHandler);
+ final RunContentDescriptor contentDescriptor =
+ new RunContentDescriptor(console, processHandler, ui.getComponent(), "Deploy to AppEngine");
+ group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_STOP_PROGRAM));
+ group.add(new CloseAction(executor, contentDescriptor, myModule.getProject()));
+
+ ExecutionManager.getInstance(myModule.getProject()).getContentManager().showRunContent(executor, contentDescriptor);
+ processHandler.startNotify();
+ }
+
+ private class FilteredOSProcessHandler extends OSProcessHandler {
+ String[] tokensToFilter;
+
+ FilteredOSProcessHandler(@NotNull final Process process, @Nullable final String commandLine, String[] filteredTokens) {
+ super(process, commandLine);
+ tokensToFilter = filteredTokens;
+ }
+
+ @Override
+ public void notifyTextAvailable(final String text, final Key outputType) {
+ String newText = text;
+ if (tokensToFilter != null) {
+ for (String token : tokensToFilter) {
+ newText = newText.replace(token, "*****");
+ }
+ }
+ super.notifyTextAvailable(newText, outputType);
+ }
+ }
+}
diff --git a/src/com/google/gct/idea/appengine/dom/AppEngineWebApp.java b/src/com/google/gct/idea/appengine/dom/AppEngineWebApp.java
new file mode 100644
index 0000000..ce3ba7d
--- /dev/null
+++ b/src/com/google/gct/idea/appengine/dom/AppEngineWebApp.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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.google.gct.idea.appengine.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericDomValue;
+
+/**
+ * This is the Dom for the App Engine config file.
+ */
+public interface AppEngineWebApp extends DomElement {
+ GenericDomValue<String> getApplication();
+ GenericDomValue<String> getVersion();
+}
diff --git a/src/com/google/gct/idea/appengine/dom/AppEngineWebFileDescription.java b/src/com/google/gct/idea/appengine/dom/AppEngineWebFileDescription.java
new file mode 100644
index 0000000..9efe86f
--- /dev/null
+++ b/src/com/google/gct/idea/appengine/dom/AppEngineWebFileDescription.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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.google.gct.idea.appengine.dom;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.xml.DomFileDescription;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This is the file description for the App Engine xml config file
+ */
+public class AppEngineWebFileDescription extends DomFileDescription<AppEngineWebApp> {
+ @NonNls public static final String APP_ENGINE_WEB_XML_NAME = "appengine-web.xml";
+
+ public AppEngineWebFileDescription() {
+ super(AppEngineWebApp.class, "appengine-web-app");
+ }
+
+ @Override
+ public boolean isMyFile(@NotNull XmlFile file, @Nullable Module module) {
+ return file.getName().equals(APP_ENGINE_WEB_XML_NAME);
+ }
+}
diff --git a/src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java b/src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java
index 48df14f..198f472 100644
--- a/src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java
+++ b/src/com/google/gct/idea/appengine/gradle/facet/AppEngineGradleFacet.java
@@ -15,6 +15,8 @@
*/
package com.google.gct.idea.appengine.gradle.facet;
+import com.google.common.base.Strings;
+import com.google.gct.idea.appengine.dom.AppEngineWebApp;
import com.intellij.facet.Facet;
import com.intellij.facet.FacetManager;
import com.intellij.facet.FacetType;
@@ -22,10 +24,18 @@ import com.intellij.facet.FacetTypeId;
import com.intellij.facet.FacetTypeRegistry;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.xml.DomManager;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.io.File;
+
/**
* App Engine Gradle facet for App Engine Modules with a Gradle build file
*/
@@ -50,6 +60,30 @@ public class AppEngineGradleFacet extends Facet<AppEngineGradleFacetConfiguratio
super(facetType, module, name, configuration, null);
}
+ /**
+ * Returns an object holding information from the appengine-web.xml file.
+ */
+ public AppEngineWebApp getAppEngineWebXml() {
+ AppEngineConfigurationProperties model = getConfiguration().getState();
+ if (model == null || Strings.isNullOrEmpty(model.WEB_APP_DIR)) {
+ return null;
+ }
+
+ String path = model.WEB_APP_DIR + "/WEB-INF/appengine-web.xml";
+ VirtualFile appEngineFile = LocalFileSystem.getInstance().findFileByPath(path.replace(File.separatorChar, '/'));
+ if (appEngineFile == null) {
+ return null;
+ }
+
+ PsiFile psiFile = PsiManager.getInstance(getModule().getProject()).findFile(appEngineFile);
+ if (psiFile == null || !(psiFile instanceof XmlFile)) {
+ return null;
+ }
+
+ final DomManager domManager = DomManager.getDomManager(getModule().getProject());
+ return domManager.getFileElement((XmlFile)psiFile, AppEngineWebApp.class).getRootElement();
+ }
+
public static FacetType<AppEngineGradleFacet, AppEngineGradleFacetConfiguration> getFacetType() {
return FacetTypeRegistry.getInstance().findFacetType(ID);
}
diff --git a/src/com/google/gct/idea/appengine/initialization/CloudPluginRegistration.java b/src/com/google/gct/idea/appengine/initialization/CloudPluginRegistration.java
new file mode 100644
index 0000000..1f103af
--- /dev/null
+++ b/src/com/google/gct/idea/appengine/initialization/CloudPluginRegistration.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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.google.gct.idea.appengine.initialization;
+
+import com.google.gct.idea.appengine.deploy.AppEngineUpdateAction;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.Anchor;
+import com.intellij.openapi.actionSystem.Constraints;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.components.ApplicationComponent;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Initializes the menus for deploy.
+ */
+public class CloudPluginRegistration implements ApplicationComponent {
+
+ // We are reusing the flag for login.
+ private final static String SHOW_DEPLOY = "show.google.login.button";
+
+ public CloudPluginRegistration() {
+ }
+
+ @Override
+ public void initComponent() {
+ if (Boolean.getBoolean(SHOW_DEPLOY)) {
+ ActionManager am = ActionManager.getInstance();
+
+ AppEngineUpdateAction action = new AppEngineUpdateAction();
+ action.getTemplatePresentation().setText("Deploy Module to App Engine...");
+
+ am.registerAction("GoogleCloudTools.AppEngineUpdate", action);
+ DefaultActionGroup buildMenu = (DefaultActionGroup)am.getAction("BuildMenu");
+
+ DefaultActionGroup appEngineUpdateGroup = new DefaultActionGroup();
+ appEngineUpdateGroup.addSeparator();
+ appEngineUpdateGroup.add(action);
+ buildMenu.add(appEngineUpdateGroup, new Constraints(Anchor.AFTER, "Compile"));
+ }
+ }
+
+ @Override
+ public void disposeComponent() {
+ }
+
+ @NotNull
+ @Override
+ public String getComponentName() {
+ return "CloudPluginRegistration";
+ }
+}