diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui.test/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/standard/StandardDeployCommandHandlerTest.java b/plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui.test/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/standard/StandardDeployCommandHandlerTest.java
index e5d233dc8a..d77527a834 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui.test/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/standard/StandardDeployCommandHandlerTest.java
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui.test/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/standard/StandardDeployCommandHandlerTest.java
@@ -20,26 +20,20 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import java.util.Collections;
-
+import com.google.cloud.tools.eclipse.test.util.ui.ExecutionEventBuilder;
+import com.google.cloud.tools.eclipse.util.FacetedProjectHelper;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
-import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.ISources;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
-import com.google.cloud.tools.eclipse.appengine.deploy.ui.standard.StandardDeployCommandHandler;
-import com.google.cloud.tools.eclipse.util.FacetedProjectHelper;
-
@RunWith(MockitoJUnitRunner.class)
public class StandardDeployCommandHandlerTest {
@@ -50,7 +44,8 @@ public void testExecute_facetedProjectCreationThrowsException() throws Execution
StandardDeployCommandHandler handler = new StandardDeployCommandHandler(facetedProjectHelper);
when(facetedProjectHelper.getFacetedProject(isA(IProject.class))).thenThrow(getFakeCoreException());
- ExecutionEvent event = getTestExecutionEvent(mock(IProject.class));
+ ExecutionEvent event = new ExecutionEventBuilder().withCurrentSelection(mock(IProject.class))
+ .withActiveShell(mock(Shell.class)).build();
handler.execute(event);
}
@@ -63,13 +58,4 @@ private Status getFakeErrorStatus() {
return new Status(IStatus.ERROR, "fakePluginId", "test exception");
}
- private ExecutionEvent getTestExecutionEvent(Object project) {
- IEvaluationContext context = mock(IEvaluationContext.class);
- IStructuredSelection selection = mock(IStructuredSelection.class);
- when(selection.size()).thenReturn(1);
- when(selection.getFirstElement()).thenReturn(project);
- when(context.getVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME)).thenReturn(selection);
- when(context.getVariable(ISources.ACTIVE_SHELL_NAME)).thenReturn(mock(Shell.class));
- return new ExecutionEvent(null /*command */, Collections.EMPTY_MAP, null /* trigger */, context);
- }
}
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/META-INF/MANIFEST.MF b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/META-INF/MANIFEST.MF
index e17851d191..788871e20f 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/META-INF/MANIFEST.MF
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/META-INF/MANIFEST.MF
@@ -12,6 +12,7 @@ Require-Bundle: org.hamcrest;bundle-version="1.1.0",
org.eclipse.jst.j2ee.web
Import-Package: com.google.cloud.tools.eclipse.test.util.project,
com.google.cloud.tools.eclipse.test.util.ui,
+ com.google.cloud.tools.eclipse.util.io,
org.mockito;provider=google;version="1.10.19",
org.mockito.runners;provider=google;version="1.10.19",
org.mockito.stubbing;provider=google;version="1.10.19",
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/EclipseContextHolder.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/EclipseContextHolder.java
index 270f199e6f..44d7d5436f 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/EclipseContextHolder.java
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/EclipseContextHolder.java
@@ -18,11 +18,10 @@
import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
-import org.junit.Rule;
import org.junit.rules.ExternalResource;
/**
- * Helper class to be used with {@link Rule} to make tests easier that depend on
+ * Helper class to be used with {@link org.junit.Rule} to make tests easier that depend on
* {@link IEclipseContext}. It provides an empty context that can be filled with object needed for
* the test using the {@link #set(Class, Object)} method.
*
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerDelegateTest.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerDelegateTest.java
index 213848ce1a..f4a892804d 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerDelegateTest.java
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerDelegateTest.java
@@ -31,6 +31,7 @@
import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.IModuleType;
import org.eclipse.wst.server.core.IRuntime;
import org.eclipse.wst.server.core.IRuntimeWorkingCopy;
import org.eclipse.wst.server.core.IServer;
@@ -154,7 +155,7 @@ public void testGetChildModules_noModuleType(){
@Test
public void testGetChildModules_nonWebModuleType(){
- ModuleType nonWebModuleType = new ModuleType("non-web", "1.0");
+ IModuleType nonWebModuleType = ModuleType.getModuleType("non-web", "1.0");
when(module1.getModuleType()).thenReturn(nonWebModuleType);
IModule[] childModules = delegate.getChildModules(new IModule[]{module1});
@@ -163,7 +164,7 @@ public void testGetChildModules_nonWebModuleType(){
@Test
public void testGetChildModules_webModuleType() {
- ModuleType webModuleType = new ModuleType("jst.web", "1.0");
+ IModuleType webModuleType = ModuleType.getModuleType("jst.web", "1.0");
when(module1.getModuleType()).thenReturn(webModuleType);
when(module1.getId()).thenReturn("module1");
when(module1.loadAdapter(IWebModule.class, null)).thenReturn(webModule1);
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchAppEngineStandardHandlerTest.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchAppEngineStandardHandlerTest.java
new file mode 100644
index 0000000000..a7586fd5e0
--- /dev/null
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchAppEngineStandardHandlerTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2016 Google 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://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.cloud.tools.eclipse.appengine.localserver.ui;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.cloud.tools.eclipse.appengine.facets.AppEngineStandardFacet;
+import com.google.cloud.tools.eclipse.test.util.project.TestProjectCreator;
+import com.google.cloud.tools.eclipse.test.util.ui.ExecutionEventBuilder;
+import com.google.common.collect.Lists;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jst.common.project.facet.core.JavaFacet;
+import org.eclipse.jst.j2ee.web.project.facet.WebFacetUtils;
+import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
+import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.IServer;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ * Tests for the {@link LaunchAppEngineStandardHandler}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class LaunchAppEngineStandardHandlerTest {
+ private static final IProjectFacetVersion APPENGINE_STANDARD_FACET_VERSION_1 =
+ ProjectFacetsManager.getProjectFacet(AppEngineStandardFacet.ID).getVersion("1");
+
+ @Rule
+ public ServerTracker tracker = new ServerTracker();
+
+ private LaunchAppEngineStandardHandler handler;
+ private IServer serverToReturn = null;
+
+ @Rule
+ public TestProjectCreator appEngineStandardProject1 =
+ new TestProjectCreator().withFacetVersions(Lists.newArrayList(JavaFacet.VERSION_1_7,
+ WebFacetUtils.WEB_25, APPENGINE_STANDARD_FACET_VERSION_1));
+ @Rule
+ public TestProjectCreator appEngineStandardProject2 =
+ new TestProjectCreator().withFacetVersions(Lists.newArrayList(JavaFacet.VERSION_1_7,
+ WebFacetUtils.WEB_25, APPENGINE_STANDARD_FACET_VERSION_1));
+
+
+ @Before
+ public void setUp() {
+ handler = new LaunchAppEngineStandardHandler() {
+ @Override
+ protected void launch(IServer server, String launchMode, SubMonitor progress)
+ throws CoreException {
+ // do nothing
+ }
+
+ @Override
+ protected IServer findExistingServer(IModule[] modules, SubMonitor progress) {
+ if (serverToReturn != null) {
+ return serverToReturn;
+ }
+ return super.findExistingServer(modules, progress);
+ }
+ };
+ }
+
+
+ @Test
+ public void testWithDefaultModule() throws ExecutionException, CoreException {
+ IModule module1 = appEngineStandardProject1.getModule();
+
+ ExecutionEvent event = new ExecutionEventBuilder().withCurrentSelection(module1).build();
+ handler.execute(event);
+ assertEquals("new server should have been created", 1, tracker.getServers().size());
+ }
+
+ @Test
+ public void testWithTwoModules() throws ExecutionException, CoreException {
+ appEngineStandardProject1.setAppEngineServiceId("default");
+ IModule module1 = appEngineStandardProject1.getModule();
+ appEngineStandardProject2.setAppEngineServiceId("other");
+ IModule module2 = appEngineStandardProject2.getModule();
+
+ ExecutionEvent event =
+ new ExecutionEventBuilder().withCurrentSelection(module1, module2).build();
+ handler.execute(event);
+ assertEquals("new server should have been created", 1, tracker.getServers().size());
+ }
+
+ @Test(expected = ExecutionException.class)
+ public void failsIfAlreadyLaunched() throws ExecutionException, CoreException {
+ IModule module1 = appEngineStandardProject1.getModule();
+
+ ExecutionEvent event = new ExecutionEventBuilder().withCurrentSelection(module1).build();
+ serverToReturn = mock(IServer.class);
+ ILaunch launch = mock(ILaunch.class);
+ when(serverToReturn.getServerState()).thenReturn(IServer.STATE_STARTED);
+ when(serverToReturn.getLaunch()).thenReturn(launch);
+ when(launch.getLaunchMode()).thenReturn(ILaunchManager.DEBUG_MODE);
+ handler.execute(event);
+ }
+
+ public void testInvariantToModuleOrder() throws ExecutionException, CoreException {
+ appEngineStandardProject1.setAppEngineServiceId("default");
+ IModule module1 = appEngineStandardProject1.getModule();
+ appEngineStandardProject2.setAppEngineServiceId("other");
+ IModule module2 = appEngineStandardProject2.getModule();
+
+ ExecutionEvent event =
+ new ExecutionEventBuilder().withCurrentSelection(module1, module2).build();
+ handler.execute(event);
+ assertEquals("new server should have been created", 1, tracker.getServers().size());
+
+ // because we don't actually launch the servers, we won't get an ExecutionException
+ ExecutionEvent swappedEvent =
+ new ExecutionEventBuilder().withCurrentSelection(module2, module1).build();
+ handler.execute(swappedEvent);
+ assertEquals("no new servers should be created", 1, tracker.getServers().size());
+ }
+
+ @Test(expected = ExecutionException.class)
+ public void failsWithClashingServiceIds() throws ExecutionException, CoreException {
+ appEngineStandardProject1.setAppEngineServiceId("other");
+ IModule module1 = appEngineStandardProject1.getModule();
+ appEngineStandardProject2.setAppEngineServiceId("other");
+ IModule module2 = appEngineStandardProject2.getModule();
+
+ ExecutionEvent event =
+ new ExecutionEventBuilder().withCurrentSelection(module1, module2).build();
+ handler.execute(event);
+ }
+
+}
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/ServerTracker.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/ServerTracker.java
new file mode 100644
index 0000000000..fd8018926d
--- /dev/null
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver.test/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/ServerTracker.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 Google 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://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.cloud.tools.eclipse.appengine.localserver.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.wst.server.core.IServer;
+import org.eclipse.wst.server.core.IServerLifecycleListener;
+import org.eclipse.wst.server.core.ServerCore;
+import org.junit.rules.ExternalResource;
+
+/** Track creation of WTP {@link IServer} instances and ensure they are deleted. */
+public class ServerTracker extends ExternalResource {
+ private List servers = Collections.synchronizedList(new ArrayList());
+
+ private IServerLifecycleListener lifecycleListener = new IServerLifecycleListener() {
+ @Override
+ public void serverAdded(IServer server) {
+ servers.add(server);
+ }
+
+ @Override
+ public void serverChanged(IServer server) {}
+
+ @Override
+ public void serverRemoved(IServer server) {
+ servers.remove(server);
+ }
+ };
+
+ @Override
+ protected void before() {
+ ServerCore.addServerLifecycleListener(lifecycleListener);
+ }
+
+ public List getServers() {
+ return servers;
+ }
+
+ @Override
+ protected void after() {
+ ServerCore.removeServerLifecycleListener(lifecycleListener);
+ for (IServer server : servers) {
+ try {
+ server.delete();
+ } catch (CoreException ex) {
+ /* ignore */
+ }
+ }
+ }
+}
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/plugin.xml b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/plugin.xml
index dcce26974e..d64ee92d86 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/plugin.xml
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/plugin.xml
@@ -222,4 +222,46 @@
typeIds="com.google.cloud.tools.eclipse.appengine.standard.server">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerDelegate.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerDelegate.java
index d81ad57c54..bee83e4a01 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerDelegate.java
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerDelegate.java
@@ -41,6 +41,11 @@
@SuppressWarnings("restriction") // For FacetUtil
public class LocalAppEngineServerDelegate extends ServerDelegate {
+ public static final String RUNTIME_TYPE_ID =
+ "com.google.cloud.tools.eclipse.appengine.standard.runtime";
+ public static final String SERVER_TYPE_ID =
+ "com.google.cloud.tools.eclipse.appengine.standard.server";
+
private static final IModule[] EMPTY_MODULES = new IModule[0];
private static final String SERVLET_MODULE_FACET = "jst.web"; //$NON-NLS-1$
private static final String ATTR_APP_ENGINE_SERVER_MODULES = "app-engine-server-modules-list"; //$NON-NLS-1$
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java
index 1904b5599c..5e3ef59d02 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java
@@ -71,6 +71,9 @@ public class LocalAppEngineServerLaunchConfigurationDelegate
private final static Logger logger =
Logger.getLogger(LocalAppEngineServerLaunchConfigurationDelegate.class.getName());
+ public static final String[] SUPPORTED_LAUNCH_MODES =
+ {ILaunchManager.RUN_MODE, ILaunchManager.DEBUG_MODE};
+
private static final String DEBUGGER_HOST = "localhost"; //$NON-NLS-1$
private static void validateCloudSdk() throws CoreException {
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/AppEngineTabGroup.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/AppEngineTabGroup.java
index db6c1c7920..50af127e58 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/AppEngineTabGroup.java
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/AppEngineTabGroup.java
@@ -16,6 +16,7 @@
package com.google.cloud.tools.eclipse.appengine.localserver.ui;
+import com.google.cloud.tools.eclipse.appengine.localserver.server.LocalAppEngineServerDelegate;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
import org.eclipse.debug.ui.EnvironmentTab;
import org.eclipse.debug.ui.ILaunchConfigurationDialog;
@@ -25,9 +26,7 @@
public class AppEngineTabGroup extends AbstractLaunchConfigurationTabGroup {
- private static final String[] SERVER_TYPE_IDS = new String[]{
- "com.google.cloud.tools.eclipse.appengine.standard.server"
- };
+ private static final String[] SERVER_TYPE_IDS = {LocalAppEngineServerDelegate.SERVER_TYPE_ID};
@Override
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchAppEngineStandardHandler.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchAppEngineStandardHandler.java
new file mode 100644
index 0000000000..e24adac141
--- /dev/null
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchAppEngineStandardHandler.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2016 Google 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://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.cloud.tools.eclipse.appengine.localserver.ui;
+
+import com.google.cloud.tools.eclipse.appengine.localserver.server.LocalAppEngineServerDelegate;
+import com.google.cloud.tools.eclipse.util.AdapterUtil;
+import com.google.cloud.tools.eclipse.util.status.StatusUtil;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.IServer;
+import org.eclipse.wst.server.core.IServerType;
+import org.eclipse.wst.server.core.IServerWorkingCopy;
+import org.eclipse.wst.server.core.ServerCore;
+import org.eclipse.wst.server.core.ServerUtil;
+
+/** Find or create a server with the selected projects and launch it. */
+public class LaunchAppEngineStandardHandler extends AbstractHandler {
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ String launchMode = event.getParameter("launchMode");
+ if (launchMode == null) {
+ launchMode = ILaunchManager.DEBUG_MODE;
+ }
+ IModule[] modules = asModules(event);
+ SubMonitor progress = SubMonitor.convert(null, 10);
+ try {
+ IServer server = findExistingServer(modules, progress.newChild(3));
+ if (server != null && isRunning(server)) {
+ ILaunch launch = server.getLaunch();
+ Preconditions.checkNotNull(launch, "Running server should have a launch");
+ String detail = launchMode.equals(launch.getLaunchMode())
+ ? "Server is already running"
+ : MessageFormat.format("Server is already running in \"{0}\" mode",
+ launch.getLaunchMode());
+ IStatus status = StatusUtil.info(this,
+ MessageFormat.format("\"{0}\" already running", server.getName()));
+ throw new ExecutionException(detail, new CoreException(status));
+ } else if (server == null) {
+ server = createServer(modules, progress.newChild(3));
+ }
+ launch(server, launchMode, progress.newChild(4));
+ } catch (CoreException ex) {
+ throw new ExecutionException("Unable to configure server", ex);
+ }
+ return null;
+ }
+
+ private static boolean isRunning(IServer server) {
+ return server.getServerState() == IServer.STATE_STARTED
+ || server.getServerState() == IServer.STATE_STARTING;
+ }
+
+ @VisibleForTesting
+ protected IServer findExistingServer(IModule[] modules, SubMonitor progress) {
+ if (modules.length == 1) {
+ IServer defaultServer = ServerCore.getDefaultServer(modules[0]);
+ if (defaultServer != null && LocalAppEngineServerDelegate.SERVER_TYPE_ID
+ .equals(defaultServer.getServerType().getId())) {
+ return defaultServer;
+ }
+ }
+ Set myModules = ImmutableSet.copyOf(modules);
+ // Look for servers that contain these modules
+ // Could prioritize servers that have *exactly* these modules,
+ // or that have the smallest overlap
+ for (IServer server : ServerCore.getServers()) {
+ if (!LocalAppEngineServerDelegate.SERVER_TYPE_ID.equals(server.getServerType().getId())) {
+ continue;
+ }
+ Set serverModules = ImmutableSet.copyOf(server.getModules());
+ if (Sets.intersection(myModules, serverModules).size() == myModules.size()) {
+ return server;
+ }
+ }
+ return null;
+ }
+
+ private IServer createServer(IModule[] modules, SubMonitor progress) throws CoreException {
+ IServerType serverType = ServerCore.findServerType(LocalAppEngineServerDelegate.SERVER_TYPE_ID);
+ IServerWorkingCopy serverWorkingCopy =
+ serverType.createServer(null, null, progress.newChild(4));
+ serverWorkingCopy.modifyModules(modules, null, progress.newChild(4));
+ return serverWorkingCopy.save(false, progress.newChild(2));
+ }
+
+ @VisibleForTesting
+ protected void launch(IServer server, String launchMode, SubMonitor progress)
+ throws CoreException {
+ server.start(launchMode, progress);
+ }
+
+ /** Identify the relevant modules from the execution context. */
+ private static IModule[] asModules(ExecutionEvent event) throws ExecutionException {
+ // First check the current selected objects
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+ if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
+ Object[] selectedObjects = ((IStructuredSelection) selection).toArray();
+ List modules = new ArrayList<>(selectedObjects.length);
+ for (Object object : selectedObjects) {
+ modules.add(asModule(object));
+ }
+ return modules.toArray(new IModule[modules.size()]);
+ }
+ // Check the project of the active editor.
+ IEditorPart editor = HandlerUtil.getActiveEditor(event);
+ if (editor != null && editor.getEditorInput() instanceof IFileEditorInput) {
+ IFileEditorInput input = (IFileEditorInput) editor.getEditorInput();
+ IProject project = input.getFile().getProject();
+ if (project != null) {
+ return new IModule[] {asModule(project)};
+ }
+ }
+ throw new ExecutionException("Cannot determine server execution context");
+ }
+
+ private static IModule asModule(Object object) throws ExecutionException {
+ IModule module = AdapterUtil.adapt(object, IModule.class);
+ if (module != null) {
+ return module;
+ }
+ IProject project = AdapterUtil.adapt(object, IProject.class);
+ if (project != null) {
+ module = ServerUtil.getModule(project);
+ if (module != null) {
+ return module;
+ }
+ }
+ throw new ExecutionException("no module found for " + object);
+ }
+
+}
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchModes.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchModes.java
new file mode 100644
index 0000000000..7118f58415
--- /dev/null
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/LaunchModes.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Google 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://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.cloud.tools.eclipse.appengine.localserver.ui;
+
+import com.google.cloud.tools.eclipse.appengine.localserver.server.LocalAppEngineServerLaunchConfigurationDelegate;
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.core.commands.IParameterValues;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.ILaunchMode;
+
+/**
+ * A helper class for the Eclipse UI that provides completions for the parameters for
+ * LocalAppEngineServer*-supported launch modes.
+ */
+public class LaunchModes implements IParameterValues {
+ @Override
+ public Map getParameterValues() {
+ ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
+ Map modes = new HashMap<>();
+ for (String modeId : LocalAppEngineServerLaunchConfigurationDelegate.SUPPORTED_LAUNCH_MODES) {
+ ILaunchMode mode = manager.getLaunchMode(modeId);
+ if (mode != null) {
+ // label is intended to be shown in menus and buttons and often has
+ // embedded '&' for mnemonics, which isn't useful here
+ String label = mode.getLabel();
+ label = label.replace("&", "");
+ modes.put(label, mode.getIdentifier());
+ }
+ }
+ return modes;
+ }
+}
diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/ServerPortExtension.java b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/ServerPortExtension.java
index 4c05e65d62..c0d45a22b4 100644
--- a/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/ServerPortExtension.java
+++ b/plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/ui/ServerPortExtension.java
@@ -18,6 +18,7 @@
import com.google.cloud.tools.eclipse.appengine.localserver.Messages;
import com.google.cloud.tools.eclipse.appengine.localserver.server.LocalAppEngineServerBehaviour;
+import com.google.cloud.tools.eclipse.appengine.localserver.server.LocalAppEngineServerDelegate;
import com.google.common.annotations.VisibleForTesting;
import java.beans.PropertyChangeEvent;
import org.eclipse.jface.fieldassist.ControlDecoration;
@@ -38,9 +39,6 @@
*/
public class ServerPortExtension extends ServerCreationWizardPageExtension {
- private static final String APP_ENGINE_SERVER_TYPE_ID =
- "com.google.cloud.tools.eclipse.appengine.standard.server"; //$NON-NLS-1$
-
@VisibleForTesting Label portLabel;
@VisibleForTesting Text portText;
@VisibleForTesting ControlDecoration portDecoration;
@@ -73,7 +71,7 @@ public void createControl(UI_POSITION position, Composite parent) {
public void handlePropertyChanged(PropertyChangeEvent event) {
if (event != null && event.getNewValue() instanceof IServerType) {
IServerType serverType = (IServerType) event.getNewValue();
- boolean showPort = APP_ENGINE_SERVER_TYPE_ID.equals(serverType.getId());
+ boolean showPort = LocalAppEngineServerDelegate.SERVER_TYPE_ID.equals(serverType.getId());
portLabel.setVisible(showPort);
portText.setVisible(showPort);
if (showPort) {
diff --git a/plugins/com.google.cloud.tools.eclipse.test.util/META-INF/MANIFEST.MF b/plugins/com.google.cloud.tools.eclipse.test.util/META-INF/MANIFEST.MF
index 811bd542e7..27b3a40fa6 100644
--- a/plugins/com.google.cloud.tools.eclipse.test.util/META-INF/MANIFEST.MF
+++ b/plugins/com.google.cloud.tools.eclipse.test.util/META-INF/MANIFEST.MF
@@ -7,9 +7,13 @@ Bundle-Vendor: %providerName
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ActivationPolicy: lazy
-Require-Bundle: org.junit;bundle-version="4.12.0"
-Import-Package: com.google.common.base;version="20.0.0",
+Require-Bundle: org.junit;bundle-version="4.12.0",
+ org.eclipse.ui.ide
+Import-Package: com.google.cloud.tools.eclipse.appengine.facets,
+ com.google.common.base;version="20.0.0",
javax.servlet,
+ org.eclipse.core.commands,
+ org.eclipse.core.expressions,
org.eclipse.core.resources,
org.eclipse.core.runtime;version="3.5.0",
org.eclipse.core.runtime.jobs,
@@ -19,11 +23,14 @@ Import-Package: com.google.common.base;version="20.0.0",
org.eclipse.jetty.server;version="9.2.13",
org.eclipse.jetty.server.handler;version="9.2.13",
org.eclipse.jetty.util.component;version="9.2.13",
+ org.eclipse.jface.viewers,
org.eclipse.swt.widgets,
org.eclipse.wst.common.project.facet.core,
org.eclipse.wst.common.project.facet.core.internal,
org.eclipse.wst.common.project.facet.core.util.internal,
+ org.eclipse.wst.server.core,
org.mockito;provider=google;version="1.10.19",
+ org.mockito.stubbing;provider=google;version="1.10.19",
org.osgi.framework;version="1.8.0"
Export-Package: com.google.cloud.tools.eclipse.test.util,
com.google.cloud.tools.eclipse.test.util.http,
diff --git a/plugins/com.google.cloud.tools.eclipse.test.util/src/com/google/cloud/tools/eclipse/test/util/project/TestProjectCreator.java b/plugins/com.google.cloud.tools.eclipse.test.util/src/com/google/cloud/tools/eclipse/test/util/project/TestProjectCreator.java
index 9ebfb7564c..f194a6fdf1 100644
--- a/plugins/com.google.cloud.tools.eclipse.test.util/src/com/google/cloud/tools/eclipse/test/util/project/TestProjectCreator.java
+++ b/plugins/com.google.cloud.tools.eclipse.test.util/src/com/google/cloud/tools/eclipse/test/util/project/TestProjectCreator.java
@@ -16,11 +16,18 @@
package com.google.cloud.tools.eclipse.test.util.project;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.google.cloud.tools.eclipse.appengine.facets.WebProjectUtil;
import com.google.common.base.Strings;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ResourcesPlugin;
@@ -34,12 +41,15 @@
import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
import org.eclipse.wst.common.project.facet.core.internal.FacetedProjectNature;
+import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.ServerUtil;
import org.junit.rules.ExternalResource;
public final class TestProjectCreator extends ExternalResource {
private IJavaProject javaProject;
private String containerPath;
+ private String appEngineServiceId;
private List projectFacetVersions = new ArrayList<>();
public TestProjectCreator withClasspathContainerPath(String containerPath) {
@@ -52,6 +62,11 @@ public TestProjectCreator withFacetVersions(List projectFa
return this;
}
+ public TestProjectCreator withAppEngineServiceId(String serviceId) {
+ appEngineServiceId = serviceId;
+ return this;
+ }
+
@Override
protected void before() throws Throwable {
createJavaProject("test" + Math.random());
@@ -76,6 +91,10 @@ public IProject getProject() {
return javaProject.getProject();
}
+ public IModule getModule() {
+ return ServerUtil.getModule(javaProject.getProject());
+ }
+
private void createJavaProject(String projectName) throws CoreException, JavaModelException {
IProjectDescription newProjectDescription = ResourcesPlugin.getWorkspace().newProjectDescription(projectName);
newProjectDescription.setNatureIds(new String[]{JavaCore.NATURE_ID, FacetedProjectNature.NATURE_ID});
@@ -86,6 +105,9 @@ private void createJavaProject(String projectName) throws CoreException, JavaMod
addContainerPathToRawClasspath();
addFacets();
+ if (appEngineServiceId != null) {
+ setAppEngineServiceId(appEngineServiceId);
+ }
}
private void addContainerPathToRawClasspath() throws JavaModelException {
@@ -106,4 +128,19 @@ private void addFacets() throws CoreException {
}
}
}
+
+ public void setAppEngineServiceId(String serviceId) throws CoreException {
+ IFolder webinf = WebProjectUtil.getWebInfDirectory(getProject());
+ IFile descriptorFile = webinf.getFile("appengine-web.xml");
+ assertTrue("Project should have AppEngine Standard facet", descriptorFile.exists());
+ StringBuilder newAppEngineWebDescriptor = new StringBuilder();
+ newAppEngineWebDescriptor
+ .append("\n");
+ newAppEngineWebDescriptor.append("").append(serviceId).append("\n");
+ newAppEngineWebDescriptor.append("\n");
+ InputStream contents = new ByteArrayInputStream(
+ newAppEngineWebDescriptor.toString().getBytes(StandardCharsets.UTF_8));
+ descriptorFile.setContents(contents, IFile.FORCE, null);
+ }
+
}
diff --git a/plugins/com.google.cloud.tools.eclipse.test.util/src/com/google/cloud/tools/eclipse/test/util/ui/ExecutionEventBuilder.java b/plugins/com.google.cloud.tools.eclipse.test.util/src/com/google/cloud/tools/eclipse/test/util/ui/ExecutionEventBuilder.java
new file mode 100644
index 0000000000..103ca8ea78
--- /dev/null
+++ b/plugins/com.google.cloud.tools.eclipse.test.util/src/com/google/cloud/tools/eclipse/test/util/ui/ExecutionEventBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Google 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://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.cloud.tools.eclipse.test.util.ui;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.ISources;
+
+/**
+ * Mocks up an {@link ExecutionEvent} object for use with Eclipse Commands/Handlers.
+ */
+public class ExecutionEventBuilder {
+
+ private IEvaluationContext context;
+
+ public ExecutionEventBuilder() {
+ context = mock(IEvaluationContext.class);
+ }
+
+ public ExecutionEvent build() {
+ return new ExecutionEvent(null /* command */, Collections.EMPTY_MAP, null /* trigger */,
+ context);
+ }
+
+ public ExecutionEventBuilder withActiveShell(Shell shell) {
+ when(context.getVariable(ISources.ACTIVE_SHELL_NAME)).thenReturn(shell);
+ return this;
+ }
+
+ public ExecutionEventBuilder withCurrentSelection(Object... objects) {
+ return withCurrentSelection(new StructuredSelection(objects));
+ }
+
+ public ExecutionEventBuilder withCurrentSelection(IStructuredSelection selection) {
+ when(context.getVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME)).thenReturn(selection);
+ return this;
+ }
+}
diff --git a/plugins/com.google.cloud.tools.eclipse.util/src/com/google/cloud/tools/eclipse/util/status/StatusUtil.java b/plugins/com.google.cloud.tools.eclipse.util/src/com/google/cloud/tools/eclipse/util/status/StatusUtil.java
index da57988e38..f92e7eae16 100644
--- a/plugins/com.google.cloud.tools.eclipse.util/src/com/google/cloud/tools/eclipse/util/status/StatusUtil.java
+++ b/plugins/com.google.cloud.tools.eclipse.util/src/com/google/cloud/tools/eclipse/util/status/StatusUtil.java
@@ -29,44 +29,44 @@ public class StatusUtil {
private StatusUtil() {}
- public static IStatus error(Class> origin, String message) {
- return error(origin, message, null);
+ public static IStatus error(Object origin, String message) {
+ return new Status(IStatus.ERROR, getBundleId(origin), message);
}
- public static IStatus error(Class> origin, String message, Throwable error) {
- String bundleOrClassname = null;
-
- Bundle bundle = FrameworkUtil.getBundle(origin);
- if (bundle == null) {
- bundleOrClassname = origin.getName();
- } else {
- bundleOrClassname = bundle.getSymbolicName();
- }
- return error(bundleOrClassname, message, error);
+ public static IStatus error(Object origin, String message, Throwable error) {
+ return new Status(IStatus.ERROR, getBundleId(origin), message, error);
}
- public static IStatus error(Object origin, String message) {
- if (origin instanceof Class) {
- return error((Class>) origin, message);
- } else {
- return error(origin.getClass(), message);
- }
+ public static IStatus warn(Object origin, String message) {
+ return new Status(IStatus.WARNING, getBundleId(origin), message);
}
- public static IStatus error(Object origin, String message, Throwable error) {
- if (origin instanceof Class) {
- return error((Class>) origin, message, error);
- } else {
- return error(origin.getClass(), message, error);
- }
+ public static IStatus warn(Object origin, String message, Throwable error) {
+ return new Status(IStatus.WARNING, getBundleId(origin), message, error);
+ }
+
+ public static IStatus info(Object origin, String message) {
+ return new Status(IStatus.INFO, getBundleId(origin), message);
+ }
+
+ public static IStatus info(Object origin, String message, Throwable error) {
+ return new Status(IStatus.INFO, getBundleId(origin), message, error);
}
- private static IStatus error(String bundleOrClassname, String message, Throwable error) {
- if (error == null) {
- return new Status(IStatus.ERROR, bundleOrClassname, message);
+ private static String getBundleId(Object origin) {
+ Class> clazz = null;
+ if (origin == null) {
+ clazz = StatusUtil.class;
+ } else if (origin instanceof Class>) {
+ clazz = (Class>) origin;
} else {
- return new Status(IStatus.ERROR, bundleOrClassname, message, error);
+ clazz = origin.getClass();
}
- }
+ Bundle bundle = FrameworkUtil.getBundle(clazz);
+ if (bundle == null) {
+ return clazz.getName(); // what else can we do?
+ }
+ return bundle.getSymbolicName();
+ }
}