@@ -42,6 +42,7 @@ import org.codehaus.groovy.grails.cli.support.PluginPathDiscoverySupport
4242abstract class ForkedGrailsProcess {
4343
4444 public static final String DEBUG_FORK = " grails.debug.fork"
45+ public static final String PARENT_PROCESS_PORT = " grails.fork.parent.process.port"
4546 public static final int DEFAULT_DAEMON_PORT = 8091
4647 public static final String DEFAULT_DEBUG_ARGS = " -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"
4748
@@ -291,27 +292,41 @@ abstract class ForkedGrailsProcess {
291292 if (daemon && ! connectedToDaemon) {
292293 GrailsConsole . instance. updateStatus(" Running without daemon..." )
293294 }
294- String classpathString = getBoostrapClasspath(executionContext)
295- List<String > cmd = buildProcessCommand(executionContext, classpathString)
296295
297- def processBuilder = new ProcessBuilder ()
298- processBuilder
299- .directory(executionContext. getBaseDir())
300- .redirectErrorStream(false )
301- .command(cmd)
296+ ServerSocket parentAvailabilityServer = new ServerSocket (0 )
297+ def parentPort = parentAvailabilityServer. localPort
298+ System . setProperty(PARENT_PROCESS_PORT , String . valueOf(parentPort))
302299
303- def process = processBuilder. start()
300+ try {
301+ String classpathString = getBoostrapClasspath(executionContext)
302+ List<String > cmd = buildProcessCommand(executionContext, classpathString)
304303
305- if (isForkingReserveEnabled()) {
306- List<String > reserveCmd = buildProcessCommand(executionContext, classpathString, true )
307- forkReserveProcess(reserveCmd, executionContext)
308- }
309- else if (shouldRunWithDaemon()) {
310- GrailsConsole . instance. updateStatus(" Starting daemon..." )
311- forkDaemon(executionContext)
312- }
313304
314- return attachOutputListener(process)
305+ def processBuilder = new ProcessBuilder ()
306+ processBuilder
307+ .directory(executionContext. getBaseDir())
308+ .redirectErrorStream(false )
309+ .command(cmd)
310+
311+ def process = processBuilder. start()
312+
313+ if (isForkingReserveEnabled()) {
314+ List<String > reserveCmd = buildProcessCommand(executionContext, classpathString, true )
315+ forkReserveProcess(reserveCmd, executionContext)
316+ }
317+ else if (shouldRunWithDaemon()) {
318+ GrailsConsole . instance. updateStatus(" Starting daemon..." )
319+ forkDaemon(executionContext)
320+ }
321+
322+ return attachOutputListener(process)
323+ } finally {
324+ try {
325+ parentAvailabilityServer?. close()
326+ } catch (e) {
327+ // ignore
328+ }
329+ }
315330 }
316331 else {
317332 return null
@@ -478,6 +493,11 @@ abstract class ForkedGrailsProcess {
478493
479494 @CompileStatic
480495 protected List<String > buildProcessCommand (ExecutionContext executionContext , String classpathString , boolean isReserve = false , boolean isDaemon = false ) {
496+ String additionalClasspath = System . getProperty(' GRAILS_ADDITIONAL_CLASSPATH' )
497+ if (additionalClasspath) {
498+ classpathString = classpathString + File . pathSeparator + additionalClasspath
499+ }
500+
481501 File tempFile = storeExecutionContext(executionContext)
482502 final javaHomeEnv = System . getenv(" JAVA_HOME" )
483503
@@ -498,8 +518,12 @@ abstract class ForkedGrailsProcess {
498518 cmd. addAll(jvmArgs)
499519 }
500520
501- cmd. addAll([" -Xmx${ maxMemory} M" . toString(), " -Xms${ minMemory} M" . toString(),
502- " -XX:MaxPermSize=${ maxPerm} m" . toString(), " -Dgrails.fork.active=true" ,
521+ cmd. addAll([" -Xmx${ maxMemory} M" . toString(), " -Xms${ minMemory} M" . toString()])
522+ if (! (System . getProperty(" java.version" ) =~ / 1.[89]./ )) {
523+ cmd. add(" -XX:MaxPermSize=${ maxPerm} m" . toString())
524+ }
525+ cmd << " -D${ PARENT_PROCESS_PORT} =${ System.getProperty(PARENT_PROCESS_PORT)} " . toString()
526+ cmd. addAll([" -Dgrails.fork.active=true" ,
503527 " -Dgrails.build.execution.context=${ tempFile.canonicalPath} " . toString(), " -cp" , classpathString])
504528
505529 if (isDebugForkEnabled() && ! isReserve) {
@@ -743,9 +767,46 @@ abstract class ForkedGrailsProcess {
743767
744768 BuildSettingsHolder . settings = buildSettings
745769 configureFork(buildSettings)
770+ startParentPortMonitor()
771+
746772 buildSettings
747773 }
748774
775+ protected void startParentPortMonitor () {
776+ def parentProcessPort = System . getProperty(PARENT_PROCESS_PORT )
777+ if (parentProcessPort) {
778+ Thread . start {
779+ def portInt = parentProcessPort. toInteger()
780+ while (true ) {
781+ sleep(15000 )
782+ if (! isServerRunning(portInt)) {
783+ // parent process killed, so bail out too
784+ GrailsConsole . getInstance(). addStatus(" Parent process shutdown. Exiting..." )
785+ System . exit(1 )
786+ }
787+ }
788+ }
789+ }
790+ }
791+
792+ /**
793+ * @return Whether the server is running
794+ */
795+ boolean isServerRunning (int port ) {
796+ Socket socket = null
797+ try {
798+ socket = new Socket (" localhost" , port)
799+ return socket. isConnected()
800+ } catch (e) {
801+ return false
802+ }
803+ finally {
804+ try {
805+ socket?. close()
806+ } catch (Throwable e) {
807+ }
808+ }
809+ }
749810 protected void configureFork (BuildSettings buildSettings ) {
750811 final runConfig = buildSettings. forkSettings. run
751812 if (runConfig instanceof Map )
0 commit comments