@@ -43,6 +43,7 @@ import org.codehaus.groovy.grails.cli.support.PluginPathDiscoverySupport
4343abstract class ForkedGrailsProcess {
4444
4545 public static final String DEBUG_FORK = " grails.debug.fork"
46+ public static final String PARENT_PROCESS_PORT = " grails.fork.parent.process.port"
4647 public static final int DEFAULT_DAEMON_PORT = 8091
4748 public static final String DEFAULT_DEBUG_ARGS = " -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"
4849
@@ -292,31 +293,41 @@ abstract class ForkedGrailsProcess {
292293 if (daemon && ! connectedToDaemon) {
293294 GrailsConsole . instance. updateStatus(" Running without daemon..." )
294295 }
295- String classpathString = getBoostrapClasspath(executionContext)
296- String additionalClasspath = System . getProperty(' GRAILS_ADDITIONAL_CLASSPATH' )
297- if (additionalClasspath) {
298- classpathString = classpathString + File . pathSeparator + additionalClasspath
299- }
300- List<String > cmd = buildProcessCommand(executionContext, classpathString)
301296
302- def processBuilder = new ProcessBuilder ()
303- processBuilder
304- .directory(executionContext. getBaseDir())
305- .redirectErrorStream(false )
306- .command(cmd)
297+ ServerSocket parentAvailabilityServer = new ServerSocket (0 )
298+ def parentPort = parentAvailabilityServer. localPort
299+ System . setProperty(PARENT_PROCESS_PORT , String . valueOf(parentPort))
307300
308- def process = processBuilder. start()
301+ try {
302+ String classpathString = getBoostrapClasspath(executionContext)
303+ List<String > cmd = buildProcessCommand(executionContext, classpathString)
309304
310- if (isForkingReserveEnabled()) {
311- List<String > reserveCmd = buildProcessCommand(executionContext, classpathString, true )
312- forkReserveProcess(reserveCmd, executionContext)
313- }
314- else if (shouldRunWithDaemon()) {
315- GrailsConsole . instance. updateStatus(" Starting daemon..." )
316- forkDaemon(executionContext)
317- }
318305
319- return attachOutputListener(process)
306+ def processBuilder = new ProcessBuilder ()
307+ processBuilder
308+ .directory(executionContext. getBaseDir())
309+ .redirectErrorStream(false )
310+ .command(cmd)
311+
312+ def process = processBuilder. start()
313+
314+ if (isForkingReserveEnabled()) {
315+ List<String > reserveCmd = buildProcessCommand(executionContext, classpathString, true )
316+ forkReserveProcess(reserveCmd, executionContext)
317+ }
318+ else if (shouldRunWithDaemon()) {
319+ GrailsConsole . instance. updateStatus(" Starting daemon..." )
320+ forkDaemon(executionContext)
321+ }
322+
323+ return attachOutputListener(process)
324+ } finally {
325+ try {
326+ parentAvailabilityServer?. close()
327+ } catch (e) {
328+ // ignore
329+ }
330+ }
320331 }
321332 else {
322333 return null
@@ -478,6 +489,11 @@ abstract class ForkedGrailsProcess {
478489
479490 @CompileStatic
480491 protected List<String > buildProcessCommand (ExecutionContext executionContext , String classpathString , boolean isReserve = false , boolean isDaemon = false ) {
492+ String additionalClasspath = System . getProperty(' GRAILS_ADDITIONAL_CLASSPATH' )
493+ if (additionalClasspath) {
494+ classpathString = classpathString + File . pathSeparator + additionalClasspath
495+ }
496+
481497 File tempFile = storeExecutionContext(executionContext)
482498 final javaHomeEnv = System . getenv(" JAVA_HOME" )
483499
@@ -501,7 +517,8 @@ abstract class ForkedGrailsProcess {
501517 cmd. addAll([" -Xmx${ maxMemory} M" . toString(), " -Xms${ minMemory} M" . toString()])
502518 if (! (System . getProperty(" java.version" ) =~ / 1.[89]./ )) {
503519 cmd. add(" -XX:MaxPermSize=${ maxPerm} m" . toString())
504- }
520+ }
521+ cmd << " -D${ PARENT_PROCESS_PORT} =${ System.getProperty(PARENT_PROCESS_PORT)} " . toString()
505522 cmd. addAll([" -Dgrails.fork.active=true" ,
506523 " -Dgrails.build.execution.context=${ tempFile.canonicalPath} " . toString(), " -cp" , classpathString])
507524
@@ -744,9 +761,46 @@ abstract class ForkedGrailsProcess {
744761
745762 BuildSettingsHolder . settings = buildSettings
746763 configureFork(buildSettings)
764+ startParentPortMonitor()
765+
747766 buildSettings
748767 }
749768
769+ protected void startParentPortMonitor () {
770+ def parentProcessPort = System . getProperty(PARENT_PROCESS_PORT )
771+ if (parentProcessPort) {
772+ Thread . start {
773+ def portInt = parentProcessPort. toInteger()
774+ while (true ) {
775+ sleep(15000 )
776+ if (! isServerRunning(portInt)) {
777+ // parent process killed, so bail out too
778+ GrailsConsole . getInstance(). addStatus(" Parent process shutdown. Exiting..." )
779+ System . exit(1 )
780+ }
781+ }
782+ }
783+ }
784+ }
785+
786+ /**
787+ * @return Whether the server is running
788+ */
789+ boolean isServerRunning (int port ) {
790+ Socket socket = null
791+ try {
792+ socket = new Socket (" localhost" , port)
793+ return socket. isConnected()
794+ } catch (e) {
795+ return false
796+ }
797+ finally {
798+ try {
799+ socket?. close()
800+ } catch (Throwable e) {
801+ }
802+ }
803+ }
750804 protected void configureFork (BuildSettings buildSettings ) {
751805 final runConfig = buildSettings. forkSettings. run
752806 if (runConfig instanceof Map )
0 commit comments