Permalink
Browse files

Better failure handling.

  • Loading branch information...
1 parent 985c637 commit 7503e23ba3de660f638629d5c03a00e3c7b257a7 @alkemist alkemist committed Nov 15, 2012
View
@@ -7,6 +7,8 @@ group = 'org.grails'
version = '2.0.0-SNAPSHOT'
archivesBaseName = "grails-gradle-plugin"
+sourceCompatibility = "1.6"
+
repositories {
maven { url "http://repo.grails.org/grails/libs-snapshots-local" }
maven { url "http://repo.grails.org/grails/repo" }
@@ -29,7 +29,9 @@
import org.gradle.process.internal.WorkerProcessBuilder;
import org.grails.launcher.context.GrailsLaunchContext;
+import java.util.Collections;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
public class ForkingGrailsLauncher {
@@ -51,38 +53,63 @@ public int launch(GrailsLaunchContext launchContext, LogLevel logLevel, Action<J
javaForkOptionsAction.execute(javaCommand);
}
- WorkerProcess workerProcess = builder.build();
+ final WorkerProcess workerProcess = builder.build();
+ final CountDownLatch latch = new CountDownLatch(1);
- DefaultGrailsForkHandle forkHandle = new DefaultGrailsForkHandle();
+ final GrailsExecutionResultHolder resultHolder = new GrailsExecutionResultHolder(latch);
workerProcess.start();
ObjectConnection connection = workerProcess.getConnection();
- connection.addIncoming(GrailsForkHandle.class, forkHandle);
GrailsWorkerHandle workerHandle = connection.addOutgoing(GrailsWorkerHandle.class);
+ connection.addIncoming(Action.class, resultHolder);
+
+ final AtomicBoolean alreadyStopped = new AtomicBoolean(false);
+
+ Thread watchForStopThread = new Thread("Gradle Process Stop Watcher") {
+ @Override
+ public void run() {
+ try {
+ ExecResult result = workerProcess.waitForStop();
+ alreadyStopped.set(true);
+ if (resultHolder.getExitCode() == null) {
+ resultHolder.execute(Collections.singletonMap("exitCode", result.getExitValue()));
+ }
+ } catch (ExecException e) {
+ if (resultHolder.getExitCode() == null) { // forcibly exited
+ resultHolder.execute(Collections.singletonMap("exitCode", 1));
+ }
+ alreadyStopped.set(true);
+ }
+ }
+ };
+ watchForStopThread.setDaemon(true);
+ watchForStopThread.start();
workerHandle.launch(launchContext);
try {
- ExecResult result = workerProcess.waitForStop();
- if (result.getExitValue() != 0) { // forcibly exited
- return result.getExitValue();
- }
- } catch (ExecException e) {
- return 1;
+ latch.await();
+ } catch (InterruptedException e) {
+ throw UncheckedException.throwAsUncheckedException(e);
}
- Throwable exception = forkHandle.getInitialisationException();
- if (exception != null) {
- throw UncheckedException.throwAsUncheckedException(exception);
+ Throwable exception = resultHolder.getInitialisationException();
+ if (exception == null) {
+ exception = resultHolder.getExecutionException();
}
- exception = forkHandle.getExecutionException();
- if (exception != null) {
- throw UncheckedException.throwAsUncheckedException(exception);
+ try {
+ if (exception == null) {
+ return resultHolder.getExitCode();
+ } else {
+ throw UncheckedException.throwAsUncheckedException(exception);
+ }
+ } finally {
+ if (!alreadyStopped.get()) {
+ workerHandle.stop();
+ }
}
-
- return forkHandle.getExitCode();
}
}
@@ -16,36 +16,37 @@
package org.grails.gradle.plugin.internal.worker;
-public class DefaultGrailsForkHandle implements GrailsForkHandle {
+import org.gradle.api.Action;
- int exitCode;
- Throwable executionException;
- Throwable initialisationException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
- @Override
- public void onExit(int exitCode) {
- this.exitCode = exitCode;
- }
+public class GrailsExecutionResultHolder implements Action<Object> {
- @Override
- public void onExecutionException(Throwable executionException) {
- this.executionException = executionException;
+ private final CountDownLatch latch;
+ private Map<String, Object> result = Collections.emptyMap();
+
+ public GrailsExecutionResultHolder(CountDownLatch latch) {
+ this.latch = latch;
}
@Override
- public void onInitialisationException(Throwable initialisationException) {
- this.initialisationException = initialisationException;
+ public void execute(Object result) {
+ //noinspection unchecked
+ this.result = (Map<String, Object>) result;
+ latch.countDown();
}
- public int getExitCode() {
- return exitCode;
+ public Integer getExitCode() {
+ return (Integer) result.get("exitCode");
}
public Throwable getExecutionException() {
- return executionException;
+ return (Throwable) result.get("executionException");
}
public Throwable getInitialisationException() {
- return initialisationException;
+ return (Throwable) result.get("initialisationException");
}
}
@@ -18,7 +18,7 @@
public interface GrailsForkHandle {
- void onExit(int returnCode);
+ boolean onExit(int returnCode);
void onExecutionException(Throwable e);
@@ -27,16 +27,22 @@
import org.grails.launcher.rootloader.RootLoaderFactory;
import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
public class GrailsServer implements Action<WorkerProcessContext>, Serializable {
@Override
public void execute(WorkerProcessContext context) {
- final GrailsForkHandle forkHandle = context.getServerConnection().addOutgoing(GrailsForkHandle.class);
+ @SuppressWarnings("unchecked")
+ final Action<Map<String, ?>> forkHandle = context.getServerConnection().addOutgoing(Action.class);
+
+ @SuppressWarnings("Convert2Diamond")
+ final Map<String, Object> result = new HashMap<String, Object>(1);
final CountDownLatch latch = new CountDownLatch(1);
+
GrailsWorkerHandle workerHandle = new GrailsWorkerHandle() {
@Override
public void launch(final GrailsLaunchContext launchContext) {
@@ -45,18 +51,24 @@ public void launch(final GrailsLaunchContext launchContext) {
RootLoader rootLoader = new RootLoaderFactory().create(launchContext, parentLoader);
GrailsLauncher grailsLauncher = new ReflectiveGrailsLauncher(rootLoader);
+ System.setProperty("grails.console.enable.terminal", "false");
+ System.setProperty("grails.console.enable.interactive", "false");
+
try {
- int exitCode = grailsLauncher.launch(launchContext);
- forkHandle.onExit(exitCode);
+ result.put("exitCode", grailsLauncher.launch(launchContext));
} catch (Throwable e) {
- forkHandle.onExecutionException(e);
+ result.put("executionException", e);
}
} catch (Throwable e) {
- e.printStackTrace();
- forkHandle.onInitialisationException(e);
- } finally {
- latch.countDown();
+ result.put("initialisationException", e);
}
+
+ forkHandle.execute(result);
+ }
+
+ @Override
+ public void stop() {
+ latch.countDown();
}
};
@@ -22,4 +22,6 @@
void launch(GrailsLaunchContext launchContext);
+ void stop();
+
}
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * 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 org.grails.gradle.plugin.integ
+
+class FailureSpec extends IntegSpec {
+
+ def "handles failure"() {
+ given:
+ applyPlugin()
+ buildFile << """
+ grails.grailsVersion '2.0.1'
+ """
+
+ buildFile << """
+ init {
+ // jvmOptions { jvmArgs "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005" }
+ }
+
+ task "package"(type: GrailsTask) {
+ //jvmOptions { jvmArgs "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5008" }
+ }
+ """
+
+ when:
+ launcher("init", "-s").run().rethrowFailure()
+ def buildConfig = file("grails-app/conf/BuildConfig.groovy")
+ buildConfig.text = buildConfig.text.replace("dependencies {", "dependencies {\ncompile('org.spockframework:spock-grails-support:0.7-groovy-1.8')")
+ println buildConfig.text
+ launcher("package", "-s").run().rethrowFailure()
+
+ then:
+ task("package").state.didWork
+
+ and:
+ file("grails-app").exists()
+ }
+}

0 comments on commit 7503e23

Please sign in to comment.