18
18
19
19
import static com .google .common .base .MoreObjects .firstNonNull ;
20
20
import static com .google .common .base .Preconditions .checkArgument ;
21
- import static java .nio .charset .StandardCharsets .UTF_8 ;
22
21
23
22
import com .google .common .base .Strings ;
24
23
import com .google .gcloud .AuthCredentials ;
30
29
import java .io .File ;
31
30
import java .io .FileInputStream ;
32
31
import java .io .FileOutputStream ;
33
- import java .io .FileReader ;
34
- import java .io .FileWriter ;
35
32
import java .io .IOException ;
36
33
import java .io .InputStream ;
37
34
import java .io .InputStreamReader ;
54
51
import java .security .NoSuchAlgorithmException ;
55
52
import java .util .ArrayList ;
56
53
import java .util .Arrays ;
57
- import java .util .HashMap ;
58
54
import java .util .List ;
59
55
import java .util .Locale ;
60
- import java .util .Map ;
61
56
import java .util .logging .Level ;
62
57
import java .util .logging .Logger ;
63
58
import java .util .regex .Pattern ;
67
62
/**
68
63
* Utility to start and stop local Google Cloud Datastore process.
69
64
*/
70
- public class LocalGcdHelper {
71
- private static final Logger log = Logger .getLogger (LocalGcdHelper .class .getName ());
72
-
73
- private final String projectId ;
74
- private Path gcdPath ;
75
- private Process startProcess ;
76
- private ProcessStreamReader processReader ;
77
- private ProcessErrorStreamReader processErrorReader ;
78
- private final int port ;
79
-
80
- public static final String DEFAULT_PROJECT_ID = "projectid1" ;
81
- public static final int DEFAULT_PORT = 8080 ;
65
+ public class LocalDatastoreHelper {
66
+ private static final Logger log = Logger .getLogger (LocalDatastoreHelper .class .getName ());
67
+ private static final double DEFAULT_CONSISTENCY = 0.9 ;
82
68
private static final String GCD_VERSION = "v1beta2" ;
83
69
private static final String GCD_BUILD = "rev1-2.1.2b" ;
84
70
private static final String GCD_BASENAME = "gcd-" + GCD_VERSION + "-" + GCD_BUILD ;
@@ -88,7 +74,13 @@ public class LocalGcdHelper {
88
74
private static final String GCLOUD = "gcloud" ;
89
75
private static final Path INSTALLED_GCD_PATH ;
90
76
private static final String GCD_VERSION_PREFIX = "gcd-emulator " ;
91
- private static final double DEFAULT_CONSISTENCY = 0.9 ;
77
+
78
+ private final String projectId ;
79
+ private Path gcdPath ;
80
+ private Process startProcess ;
81
+ private ProcessStreamReader processReader ;
82
+ private ProcessErrorStreamReader processErrorReader ;
83
+ private final int port ;
92
84
93
85
static {
94
86
INSTALLED_GCD_PATH = installedGcdPath ();
@@ -103,14 +95,6 @@ public class LocalGcdHelper {
103
95
}
104
96
}
105
97
106
- public static int findAvailablePort (int defaultPort ) {
107
- try (ServerSocket tempSocket = new ServerSocket (0 )) {
108
- return tempSocket .getLocalPort ();
109
- } catch (IOException e ) {
110
- return defaultPort ;
111
- }
112
- }
113
-
114
98
private static Path installedGcdPath () {
115
99
String gcloudExecutableName ;
116
100
if (isWindows ()) {
@@ -390,56 +374,6 @@ public static CommandWrapper create() {
390
374
}
391
375
}
392
376
393
- public LocalGcdHelper (String projectId , int port ) {
394
- this .projectId = projectId ;
395
- this .port = port ;
396
- }
397
-
398
- /**
399
- * Returns a {@link DatastoreOptions} instance that sets the host to use the Datastore emulator
400
- * on localhost.
401
- */
402
- public DatastoreOptions options () {
403
- return DatastoreOptions .builder ()
404
- .projectId (projectId )
405
- .host ("http://localhost:" + Integer .toString (port ))
406
- .authCredentials (AuthCredentials .noAuth ())
407
- .build ();
408
- }
409
-
410
- /**
411
- * Starts the local datastore for the specific project.
412
- *
413
- * This will unzip the gcd tool, create the project and start it.
414
- * All content is written to a temporary directory that will be deleted when
415
- * {@link #stop()} is called or when the program terminates) to make sure that no left-over
416
- * data from prior runs is used.
417
- *
418
- * @param consistency the fraction of job application attempts that will succeed, with 0.0
419
- * resulting in no attempts succeeding, and 1.0 resulting in all attempts succeeding. Defaults
420
- * to 0.9. Note that setting this to 1.0 may mask incorrect assumptions about the consistency
421
- * of non-ancestor queries; non-ancestor queries are eventually consistent.
422
- */
423
- public void start (double consistency ) throws IOException , InterruptedException {
424
- // send a quick request in case we have a hanging process from a previous run
425
- checkArgument (consistency >= 0.0 && consistency <= 1.0 , "Consistency must be between 0 and 1" );
426
- sendQuitRequest (port );
427
- // Each run is associated with its own folder that is deleted once test completes.
428
- gcdPath = Files .createTempDirectory ("gcd" );
429
- File gcdFolder = gcdPath .toFile ();
430
- gcdFolder .deleteOnExit ();
431
-
432
- Path gcdExecutablePath ;
433
- // If cloud is available we use it, otherwise we download and start gcd
434
- if (INSTALLED_GCD_PATH == null ) {
435
- downloadGcd ();
436
- gcdExecutablePath = gcdPath .resolve (GCD_BASENAME );
437
- } else {
438
- gcdExecutablePath = INSTALLED_GCD_PATH ;
439
- }
440
- startGcd (gcdExecutablePath , consistency );
441
- }
442
-
443
377
private void downloadGcd () throws IOException {
444
378
// check if we already have a local copy of the gcd utility and download it if not.
445
379
File gcdZipFile = new File (System .getProperty ("java.io.tmpdir" ), GCD_FILENAME );
@@ -603,83 +537,93 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
603
537
});
604
538
}
605
539
606
- public static LocalGcdHelper start (String projectId , int port , double consistency )
540
+ private LocalDatastoreHelper (String projectId ) {
541
+ this .projectId = projectId ;
542
+ this .port = findAvailablePort ();
543
+ }
544
+
545
+ private static int findAvailablePort () {
546
+ try (ServerSocket tempSocket = new ServerSocket (0 )) {
547
+ return tempSocket .getLocalPort ();
548
+ } catch (IOException e ) {
549
+ return -1 ;
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Returns a {@link DatastoreOptions} instance that sets the host to use the Datastore emulator
555
+ * on localhost.
556
+ */
557
+ public DatastoreOptions options () {
558
+ return DatastoreOptions .builder ()
559
+ .projectId (projectId )
560
+ .host ("http://localhost:" + Integer .toString (port ))
561
+ .authCredentials (AuthCredentials .noAuth ())
562
+ .build ();
563
+ }
564
+
565
+ /**
566
+ * Returns the project ID associated with this local Datastore emulator.
567
+ */
568
+ public String projectId () {
569
+ return projectId ;
570
+ }
571
+
572
+ /**
573
+ * Returns the port on localhost to which the local Datastore listens for requests.
574
+ */
575
+ public int port () {
576
+ return port ;
577
+ }
578
+
579
+ /**
580
+ * Starts the local Datastore for the specific project.
581
+ *
582
+ * This will unzip the gcd tool, create the project and start it. All content is written to a
583
+ * temporary directory that will be deleted when {@link #stop()} is called (or when the program
584
+ * terminates) to make sure that no left-over data from prior runs is used.
585
+ *
586
+ * @param consistency the fraction of Datastore writes that are immediately visible to global
587
+ * queries, with 0.0 resulting in no attempts succeeding, and 1.0 resulting in all attempts succeeding. Note
588
+ * that setting this to 1.0 may mask incorrect assumptions about the consistency of
589
+ * non-ancestor queries; non-ancestor queries are eventually consistent.
590
+ */
591
+ public static LocalDatastoreHelper start (String projectId , double consistency )
607
592
throws IOException , InterruptedException {
608
- LocalGcdHelper helper = new LocalGcdHelper (projectId , port );
593
+ LocalDatastoreHelper helper = new LocalDatastoreHelper (projectId );
609
594
helper .start (consistency );
610
595
return helper ;
611
596
}
612
597
613
- public static void main (String ... args ) throws IOException , InterruptedException {
614
- Map <String , String > parsedArgs = parseArgs (args );
615
- String action = parsedArgs .get ("action" );
616
- int port =
617
- (parsedArgs .get ("port" ) == null ) ? DEFAULT_PORT : Integer .parseInt (parsedArgs .get ("port" ));
618
- switch (action ) {
619
- case "START" :
620
- if (!isActive (DEFAULT_PROJECT_ID , port )) {
621
- double consistency = parsedArgs .get ("consistency" ) == null
622
- ? DEFAULT_CONSISTENCY : Double .parseDouble (parsedArgs .get ("consistency" ));
623
- LocalGcdHelper helper = start (DEFAULT_PROJECT_ID , port , consistency );
624
- try (FileWriter writer = new FileWriter (".local_gcd_helper" )) {
625
- writer .write (helper .gcdPath .toAbsolutePath ().toString () + System .lineSeparator ());
626
- writer .write (Integer .toString (port ));
627
- }
628
- }
629
- return ;
630
- case "STOP" :
631
- File file = new File (".local_gcd_helper" );
632
- String path = null ;
633
- boolean fileExists = file .exists ();
634
- if (fileExists ) {
635
- try (BufferedReader reader = new BufferedReader (new FileReader (file ))) {
636
- path = reader .readLine ();
637
- port = Integer .parseInt (reader .readLine ());
638
- }
639
- }
640
- sendQuitRequest (port );
641
- if (fileExists ) {
642
- deleteRecurse (Paths .get (path ));
643
- file .delete ();
644
- }
645
- return ;
646
- default :
647
- break ;
648
- }
598
+ /**
599
+ * Starts the local Datastore for the specific project using a default consistency setting of 0.9.
600
+ *
601
+ * This will unzip the gcd tool, create the project and start it. All content is written to a
602
+ * temporary directory that will be deleted when {@link #stop()} is called (or when the program
603
+ * terminates) to make sure that no left-over data from prior runs is used.
604
+ */
605
+ public static LocalDatastoreHelper start (String projectId )
606
+ throws IOException , InterruptedException {
607
+ return start (projectId , DEFAULT_CONSISTENCY );
649
608
}
650
609
651
- private static Map <String , String > parseArgs (String [] args ) {
652
- Map <String , String > parsedArgs = new HashMap <String , String >();
653
- for (String arg : args ) {
654
- if (arg .startsWith ("--port=" )) {
655
- parsedArgs .put ("port" , arg .substring ("--port=" .length ()));
656
- } else if (arg .startsWith ("--consistency=" )) {
657
- parsedArgs .put ("consistency" , arg .substring ("--consistency=" .length ()));
658
- } else if (arg .equals ("START" ) || arg .equals ("STOP" )) {
659
- parsedArgs .put ("action" , arg );
660
- } else {
661
- throw new RuntimeException ("Only accepts START, STOP, --port=<port #> and "
662
- + "--consistency=<double in range [0,1]> as arguments." );
663
- }
664
- }
665
- if (parsedArgs .get ("action" ) == null ) {
666
- throw new RuntimeException ("EXPECTING START | STOP" );
667
- }
668
- return parsedArgs ;
669
- }
610
+ private void start (double consistency ) throws IOException , InterruptedException {
611
+ // send a quick request in case we have a hanging process from a previous run
612
+ checkArgument (consistency >= 0.0 && consistency <= 1.0 , "Consistency must be between 0 and 1" );
613
+ sendQuitRequest (port );
614
+ // Each run is associated with its own folder that is deleted once test completes.
615
+ gcdPath = Files .createTempDirectory ("gcd" );
616
+ File gcdFolder = gcdPath .toFile ();
617
+ gcdFolder .deleteOnExit ();
670
618
671
- public static boolean isActive (String projectId , int port ) {
672
- try {
673
- StringBuilder urlBuilder = new StringBuilder ("http://localhost:" ).append (port );
674
- urlBuilder .append ("/datastore/v1beta2/datasets/" ).append (projectId ).append ("/lookup" );
675
- URL url = new URL (urlBuilder .toString ());
676
- try (BufferedReader reader =
677
- new BufferedReader (new InputStreamReader (url .openStream (), UTF_8 ))) {
678
- return "Valid RPC" .equals (reader .readLine ());
679
- }
680
- } catch (IOException ignore ) {
681
- // assume not active
682
- return false ;
619
+ Path gcdExecutablePath ;
620
+ // If cloud is available we use it, otherwise we download and start gcd
621
+ if (INSTALLED_GCD_PATH == null ) {
622
+ downloadGcd ();
623
+ gcdExecutablePath = gcdPath .resolve (GCD_BASENAME );
624
+ } else {
625
+ gcdExecutablePath = INSTALLED_GCD_PATH ;
683
626
}
627
+ startGcd (gcdExecutablePath , consistency );
684
628
}
685
629
}
0 commit comments