Skip to content

Commit 2b18352

Browse files
authored
Prompt the user if unpublished changes detected during launch (#2047)
- Configure the server as publishing-then-launch. - Change behaviour to check if there are pending changes to be published and prompt the user whether to continue
1 parent 5b70024 commit 2b18352

7 files changed

Lines changed: 175 additions & 13 deletions

File tree

plugins/com.google.cloud.tools.eclipse.appengine.localserver/plugin.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
runtime="true"
4343
runtimeTypeId="com.google.cloud.tools.eclipse.appengine.standard.runtime"
4444
startBeforePublish="false"
45-
supportsRemoteHosts="true">
45+
supportsRemoteHosts="true"
46+
synchronousStart="true">
4647
</serverType>
4748
</extension>
4849

@@ -322,4 +323,13 @@
322323
id="com.google.cloud.tools.eclipse.appengine.localserver.cloudSdkDebugTarget">
323324
</debugModelPresentation>
324325
</extension>
326+
<extension
327+
point="org.eclipse.debug.core.statusHandlers">
328+
<statusHandler
329+
class="com.google.cloud.tools.eclipse.appengine.localserver.ui.StaleResourcesStatusHandler"
330+
code="255"
331+
id="com.google.cloud.tools.eclipse.appengine.localserver.statusHandlers.staleResources"
332+
plugin="com.google.cloud.tools.eclipse.appengine.localserver">
333+
</statusHandler>
334+
</extension>
325335
</plugin>

plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/messages.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ CANNOT_DETERMINE_EXECUTION_CONTEXT=Cannot determine server execution context
1919
SERVER_ALREADY_RUNNING=Server is already running
2020
SERVER_ALREADY_RUNNING_IN_MODE=Server is already running in "{0}" mode
2121
UNABLE_TO_LAUNCH=Unable to launch App Engine server
22+
STALE_RESOURCES_DETECTED=Stale resources detected
23+
STALE_RESOURCES_LAUNCH_CONFIRMATION=Some recently changed resources are not yet published. Continue with launch?
2224

2325
target.terminated=<terminated>{0}
2426
cloudsdk.server.description=Google Cloud SDK Dev App Server

plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerBehaviour.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,21 @@ public final void setModulePublishState2(IModule[] module, int state) {
199199
setModulePublishState(module, state);
200200
}
201201

202+
@Override
203+
protected void publishFinish(IProgressMonitor monitor) throws CoreException {
204+
boolean allPublished = true;
205+
IServer server = getServer();
206+
IModule[] modules = server.getModules();
207+
for (IModule module : modules) {
208+
if (server.getModulePublishState(new IModule[] {module}) != IServer.PUBLISH_STATE_NONE) {
209+
allPublished = false;
210+
}
211+
}
212+
if (allPublished) {
213+
setServerPublishState(IServer.PUBLISH_STATE_NONE);
214+
}
215+
}
216+
202217
private static IStatus newErrorStatus(String message) {
203218
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
204219
}

plugins/com.google.cloud.tools.eclipse.appengine.localserver/src/com/google/cloud/tools/eclipse/appengine/localserver/server/LocalAppEngineServerLaunchConfigurationDelegate.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.cloud.tools.eclipse.appengine.localserver.Messages;
2626
import com.google.cloud.tools.eclipse.appengine.localserver.PreferencesInitializer;
2727
import com.google.cloud.tools.eclipse.appengine.localserver.ui.LocalAppEngineConsole;
28+
import com.google.cloud.tools.eclipse.appengine.localserver.ui.StaleResourcesStatusHandler;
2829
import com.google.cloud.tools.eclipse.ui.util.MessageConsoleUtilities;
2930
import com.google.cloud.tools.eclipse.ui.util.WorkbenchUtil;
3031
import com.google.cloud.tools.eclipse.usagetracker.AnalyticsEvents;
@@ -50,13 +51,16 @@
5051
import java.util.logging.Logger;
5152
import org.eclipse.core.resources.IMarkerDelta;
5253
import org.eclipse.core.resources.IProject;
54+
import org.eclipse.core.resources.ResourcesPlugin;
5355
import org.eclipse.core.runtime.CoreException;
5456
import org.eclipse.core.runtime.IPath;
5557
import org.eclipse.core.runtime.IProgressMonitor;
5658
import org.eclipse.core.runtime.IStatus;
5759
import org.eclipse.core.runtime.MultiStatus;
5860
import org.eclipse.core.runtime.Platform;
5961
import org.eclipse.core.runtime.Status;
62+
import org.eclipse.core.runtime.SubMonitor;
63+
import org.eclipse.core.runtime.jobs.Job;
6064
import org.eclipse.debug.core.DebugEvent;
6165
import org.eclipse.debug.core.DebugException;
6266
import org.eclipse.debug.core.DebugPlugin;
@@ -65,6 +69,7 @@
6569
import org.eclipse.debug.core.ILaunchConfigurationType;
6670
import org.eclipse.debug.core.ILaunchManager;
6771
import org.eclipse.debug.core.ILaunchesListener2;
72+
import org.eclipse.debug.core.IStatusHandler;
6873
import org.eclipse.debug.core.model.DebugElement;
6974
import org.eclipse.debug.core.model.IBreakpoint;
7075
import org.eclipse.debug.core.model.IDebugTarget;
@@ -82,6 +87,7 @@
8287
import org.eclipse.wst.server.core.IModule;
8388
import org.eclipse.wst.server.core.IServer;
8489
import org.eclipse.wst.server.core.IServerListener;
90+
import org.eclipse.wst.server.core.ServerCore;
8591
import org.eclipse.wst.server.core.ServerEvent;
8692
import org.eclipse.wst.server.core.ServerUtil;
8793

@@ -128,6 +134,65 @@ public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws
128134
return super.getLaunch(configuration, mode);
129135
}
130136

137+
@Override
138+
public boolean finalLaunchCheck(ILaunchConfiguration configuration, String mode,
139+
IProgressMonitor monitor) throws CoreException {
140+
SubMonitor progress = SubMonitor.convert(monitor, 40);
141+
if (!super.finalLaunchCheck(configuration, mode, progress.newChild(20))) {
142+
return false;
143+
}
144+
145+
// If we're auto-publishing before launch, check if there may be stale
146+
// resources not yet published. See
147+
// https://github.com/GoogleCloudPlatform/google-cloud-eclipse/issues/1832
148+
if (ServerCore.isAutoPublishing() && ResourcesPlugin.getWorkspace().isAutoBuilding()) {
149+
// Must wait for any current autobuild to complete so resource changes are triggered
150+
// and WTP will kick off ResourceChangeJobs. Note that there may be builds
151+
// pending that are unrelated to our resource changes, so simply checking
152+
// <code>JobManager.find(FAMILY_AUTO_BUILD).length > 0</code> produces too many
153+
// false positives.
154+
try {
155+
Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, progress.newChild(20));
156+
} catch (InterruptedException ex) {
157+
/* ignore */
158+
}
159+
IServer server = ServerUtil.getServer(configuration);
160+
if (server.shouldPublish() || hasPendingChangesToPublish()) {
161+
IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(promptStatus);
162+
if (prompter != null) {
163+
Object continueLaunch = prompter
164+
.handleStatus(StaleResourcesStatusHandler.CONTINUE_LAUNCH_REQUEST, configuration);
165+
if (!(Boolean) continueLaunch) {
166+
// cancel the launch so Server.StartJob won't raise an error dialog, since the
167+
// server won't have been started
168+
monitor.setCanceled(true);
169+
return false;
170+
}
171+
return true;
172+
}
173+
}
174+
}
175+
return true;
176+
}
177+
178+
/**
179+
* Check if there are pending changes to be published: this is a nasty condition that can occur if
180+
* the user saves changes as part of launching the server.
181+
*/
182+
private boolean hasPendingChangesToPublish() {
183+
Job[] serverJobs = Job.getJobManager().find(ServerUtil.SERVER_JOB_FAMILY);
184+
Job currentJob = Job.getJobManager().currentJob();
185+
for (Job job : serverJobs) {
186+
// Launching from Server#start() means this will be running within a
187+
// Server.StartJob. All other jobs should be ResourceChangeJob or
188+
// PublishJob, both of which indicate unpublished changes.
189+
if (job != currentJob) {
190+
return true;
191+
}
192+
}
193+
return false;
194+
}
195+
131196
@VisibleForTesting
132197
void checkConflictingLaunches(ILaunchConfigurationType launchConfigType,
133198
DefaultRunConfiguration runConfig, ILaunch[] launches) throws CoreException {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2017 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.tools.eclipse.appengine.localserver.ui;
18+
19+
import com.google.cloud.tools.eclipse.appengine.localserver.Messages;
20+
import org.eclipse.core.runtime.CoreException;
21+
import org.eclipse.core.runtime.IStatus;
22+
import org.eclipse.core.runtime.Status;
23+
import org.eclipse.debug.core.ILaunchConfiguration;
24+
import org.eclipse.debug.core.IStatusHandler;
25+
import org.eclipse.debug.internal.ui.DebugUIPlugin;
26+
import org.eclipse.debug.ui.DebugUITools;
27+
import org.eclipse.jface.dialogs.MessageDialog;
28+
import org.eclipse.swt.widgets.Shell;
29+
30+
/**
31+
* A simple prompter for the Platform/Debug framework to prompt the user to confirm whether the
32+
* launch should continue when possibly stale resources have been found.
33+
*/
34+
public class StaleResourcesStatusHandler implements IStatusHandler {
35+
/**
36+
* The error code indicating that there may be stale resources. Used with the plugin ID to
37+
* uniquely identify this prompter.
38+
*/
39+
static final int CONFIRM_LAUNCH_CODE = 255;
40+
41+
/**
42+
* A specially crafted status message that is pass into the Debug Prompter class to obtain our
43+
* confirmation prompter.
44+
*/
45+
public static final IStatus CONTINUE_LAUNCH_REQUEST = new Status(IStatus.INFO,
46+
"com.google.cloud.tools.eclipse.appengine.localserver", CONFIRM_LAUNCH_CODE, "", null);
47+
48+
@Override
49+
public Object handleStatus(IStatus status, Object source) throws CoreException {
50+
if (source instanceof ILaunchConfiguration) {
51+
ILaunchConfiguration config = (ILaunchConfiguration) source;
52+
if (DebugUITools.isPrivate(config)) {
53+
return Boolean.FALSE;
54+
}
55+
}
56+
57+
Shell activeShell = DebugUIPlugin.getShell();
58+
// should consider using MessageDialogWithToggle?
59+
return MessageDialog.openQuestion(activeShell, Messages.getString("STALE_RESOURCES_DETECTED"),
60+
Messages.getString("STALE_RESOURCES_LAUNCH_CONFIRMATION"));
61+
}
62+
}

plugins/com.google.cloud.tools.eclipse.integration.appengine/src/com/google/cloud/tools/eclipse/integration/appengine/BaseProjectTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ public static void setUp() throws Exception {
5151
@After
5252
public void tearDown() {
5353
if (project != null) {
54+
// close editors, so no property changes are dispatched on delete
55+
bot.closeAllEditors();
56+
5457
// ensure there are no jobs
5558
SwtBotWorkbenchActions.waitForProjects(bot, project);
5659
try {

plugins/com.google.cloud.tools.eclipse.integration.appengine/src/com/google/cloud/tools/eclipse/integration/appengine/DebugNativeAppEngineStandardProjectTest.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,20 @@
2020
import static org.junit.Assert.assertEquals;
2121
import static org.junit.Assert.assertFalse;
2222
import static org.junit.Assert.assertNotNull;
23+
import static org.junit.Assert.assertThat;
2324
import static org.junit.Assert.assertTrue;
2425
import static org.junit.Assert.fail;
2526

2627
import com.google.cloud.tools.eclipse.swtbot.SwtBotProjectActions;
2728
import com.google.cloud.tools.eclipse.swtbot.SwtBotTestingUtilities;
2829
import com.google.cloud.tools.eclipse.swtbot.SwtBotTreeUtilities;
2930
import com.google.cloud.tools.eclipse.test.util.ThreadDumpingWatchdog;
30-
31+
import java.io.IOException;
32+
import java.io.InputStreamReader;
33+
import java.net.URL;
34+
import java.net.URLConnection;
35+
import java.nio.charset.StandardCharsets;
36+
import java.util.concurrent.TimeUnit;
3137
import org.eclipse.core.runtime.preferences.InstanceScope;
3238
import org.eclipse.swt.custom.StyledText;
3339
import org.eclipse.swt.widgets.Tree;
@@ -38,18 +44,12 @@
3844
import org.eclipse.swtbot.swt.finder.widgets.SWTBotToolbarButton;
3945
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
4046
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
47+
import org.hamcrest.Matchers;
4148
import org.junit.Rule;
4249
import org.junit.Test;
4350
import org.junit.runner.RunWith;
4451
import org.osgi.service.prefs.Preferences;
4552

46-
import java.io.IOException;
47-
import java.io.InputStreamReader;
48-
import java.net.URL;
49-
import java.net.URLConnection;
50-
import java.nio.charset.StandardCharsets;
51-
import java.util.concurrent.TimeUnit;
52-
5353
/**
5454
* Create a native App Engine Standard project, launch in debug mode, verify working, and then
5555
* terminate.
@@ -101,16 +101,21 @@ public void run() {
101101

102102
SWTBotTree launchTree =
103103
new SWTBotTree(bot.widget(widgetOfType(Tree.class), debugView.getWidget()));
104+
105+
// avoid any stray processes that may be lying around
106+
launchTree.contextMenu("Remove All Terminated").click();
107+
104108
SwtBotTreeUtilities.waitUntilTreeHasItems(bot, launchTree);
105109
SWTBotTreeItem[] allItems = launchTree.getAllItems();
106110
SwtBotTreeUtilities.waitUntilTreeHasText(bot, allItems[0]);
107-
assertTrue("No App Engine launch found",
108-
allItems[0].getText().contains("App Engine Standard at localhost"));
111+
assertThat("No App Engine launch found", allItems[0].getText(),
112+
Matchers.containsString("App Engine Standard at localhost"));
109113

110114
SWTBotView consoleView = bot.viewById("org.eclipse.ui.console.ConsoleView"); // IConsoleConstants.ID_CONSOLE_VIEW
111115
consoleView.show();
112-
assertTrue("App Engine console not active", consoleView.getViewReference()
113-
.getContentDescription().contains("App Engine Standard at localhost"));
116+
assertThat("App Engine console not active",
117+
consoleView.getViewReference().getContentDescription(),
118+
Matchers.containsString("App Engine Standard at localhost"));
114119
final SWTBotStyledText consoleContents =
115120
new SWTBotStyledText(bot.widget(widgetOfType(StyledText.class), consoleView.getWidget()));
116121
SwtBotTestingUtilities.waitUntilStyledTextContains(bot,

0 commit comments

Comments
 (0)