3535import java .math .BigInteger ;
3636import java .net .HttpURLConnection ;
3737import java .net .MalformedURLException ;
38+ import java .net .ServerSocket ;
3839import java .net .URL ;
3940import java .nio .channels .Channels ;
4041import java .nio .channels .ReadableByteChannel ;
@@ -68,9 +69,10 @@ public class LocalGcdHelper {
6869 private final String projectId ;
6970 private Path gcdPath ;
7071 private ProcessStreamReader processReader ;
72+ private final int port ;
7173
7274 public static final String DEFAULT_PROJECT_ID = "projectid1" ;
73- public static final int PORT = 8080 ;
75+ private static final int DEFAULT_PORT = 8080 ;
7476 private static final String GCD_VERSION = "v1beta2" ;
7577 private static final String GCD_BUILD = "rev1-2.1.2b" ;
7678 private static final String GCD_BASENAME = "gcd-" + GCD_VERSION + "-" + GCD_BUILD ;
@@ -94,6 +96,17 @@ public class LocalGcdHelper {
9496 }
9597 }
9698
99+ public static int findOpenPort () {
100+ int port ;
101+ try (ServerSocket temp_socket = new ServerSocket (0 )) {
102+ port = temp_socket .getLocalPort ();
103+ temp_socket .close ();
104+ } catch (IOException e ) {
105+ port = DEFAULT_PORT ;
106+ }
107+ return port ;
108+ }
109+
97110 private static Path installedGcdPath () {
98111 String gcloudExecutableName ;
99112 if (isWindows ()) {
@@ -283,8 +296,9 @@ public static CommandWrapper create() {
283296 }
284297 }
285298
286- public LocalGcdHelper (String projectId ) {
299+ public LocalGcdHelper (String projectId , int port ) {
287300 this .projectId = projectId ;
301+ this .port = port ;
288302 }
289303
290304 /**
@@ -297,7 +311,7 @@ public LocalGcdHelper(String projectId) {
297311 */
298312 public void start () throws IOException , InterruptedException {
299313 // send a quick request in case we have a hanging process from a previous run
300- sendQuitRequest ();
314+ sendQuitRequest (port );
301315 // Each run is associated with its own folder that is deleted once test completes.
302316 gcdPath = Files .createTempDirectory ("gcd" );
303317 File gcdFolder = gcdPath .toFile ();
@@ -379,13 +393,12 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept
379393 if (log .isLoggable (Level .FINE )) {
380394 log .log (Level .FINE , "Starting datastore emulator for the project: {0}" , projectId );
381395 }
382- Process startProcess =
383- CommandWrapper .create ()
384- .command (gcdAbsolutePath .toString (), "start" , "--testing" , "--allow_remote_shutdown" ,
385- projectId )
386- .directory (gcdPath )
387- .redirectErrorStream ()
388- .start ();
396+ Process startProcess = CommandWrapper .create ()
397+ .command (gcdAbsolutePath .toString (), "start" , "--testing" , "--allow_remote_shutdown" ,
398+ "--port=" + Integer .toString (port ), projectId )
399+ .directory (gcdPath )
400+ .redirectErrorStream ()
401+ .start ();
389402 processReader = ProcessStreamReader .start (startProcess , "Dev App Server is now running" );
390403 }
391404
@@ -419,9 +432,9 @@ private static void extractFile(ZipInputStream zipIn, File filePath) throws IOEx
419432 }
420433 }
421434
422- public static void sendQuitRequest () {
435+ public static void sendQuitRequest (int port ) {
423436 try {
424- URL url = new URL ("http" , "localhost" , PORT , "/_ah/admin/quit" );
437+ URL url = new URL ("http" , "localhost" , port , "/_ah/admin/quit" );
425438 HttpURLConnection con = (HttpURLConnection ) url .openConnection ();
426439 con .setRequestMethod ("POST" );
427440 con .setDoOutput (true );
@@ -439,7 +452,7 @@ public static void sendQuitRequest() {
439452 }
440453
441454 public void stop () throws IOException , InterruptedException {
442- sendQuitRequest ();
455+ sendQuitRequest (port );
443456 if (processReader != null ) {
444457 processReader .terminate ();
445458 }
@@ -468,8 +481,8 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
468481 });
469482 }
470483
471- public static LocalGcdHelper start (String projectId ) throws IOException , InterruptedException {
472- LocalGcdHelper helper = new LocalGcdHelper (projectId );
484+ public static LocalGcdHelper start (String projectId , int port ) throws IOException , InterruptedException {
485+ LocalGcdHelper helper = new LocalGcdHelper (projectId , port );
473486 helper .start ();
474487 return helper ;
475488 }
@@ -478,15 +491,15 @@ public static void main(String... args) throws IOException, InterruptedException
478491 if (args .length == 1 ) {
479492 switch (args [0 ]) {
480493 case "START" :
481- if (!isActive (DEFAULT_PROJECT_ID )) {
482- LocalGcdHelper helper = start (DEFAULT_PROJECT_ID );
494+ if (!isActive (DEFAULT_PROJECT_ID , DEFAULT_PORT )) {
495+ LocalGcdHelper helper = start (DEFAULT_PROJECT_ID , DEFAULT_PORT );
483496 try (FileWriter writer = new FileWriter (".local_gcd_helper" )) {
484497 writer .write (helper .gcdPath .toAbsolutePath ().toString ());
485498 }
486499 }
487500 return ;
488501 case "STOP" :
489- sendQuitRequest ();
502+ sendQuitRequest (DEFAULT_PORT );
490503 File file = new File (".local_gcd_helper" );
491504 if (file .exists ()) {
492505 try (BufferedReader reader = new BufferedReader (new FileReader (file ))) {
@@ -503,9 +516,9 @@ public static void main(String... args) throws IOException, InterruptedException
503516 throw new RuntimeException ("expecting only START | STOP" );
504517 }
505518
506- public static boolean isActive (String projectId ) {
519+ public static boolean isActive (String projectId , int port ) {
507520 try {
508- StringBuilder urlBuilder = new StringBuilder ("http://localhost:" ).append (PORT );
521+ StringBuilder urlBuilder = new StringBuilder ("http://localhost:" ).append (port );
509522 urlBuilder .append ("/datastore/v1beta2/datasets/" ).append (projectId ).append ("/lookup" );
510523 URL url = new URL (urlBuilder .toString ());
511524 try (BufferedReader reader =
0 commit comments