2020
2121import com .google .common .base .Strings ;
2222
23+ import java .io .BufferedInputStream ;
2324import java .io .BufferedOutputStream ;
2425import java .io .BufferedReader ;
2526import java .io .File ;
27+ import java .io .FileInputStream ;
2628import java .io .FileOutputStream ;
2729import java .io .FileReader ;
2830import java .io .FileWriter ;
2931import java .io .IOException ;
3032import java .io .InputStream ;
3133import java .io .InputStreamReader ;
3234import java .io .OutputStream ;
35+ import java .math .BigInteger ;
3336import java .net .HttpURLConnection ;
37+ import java .net .MalformedURLException ;
3438import java .net .URL ;
39+ import java .nio .channels .Channels ;
40+ import java .nio .channels .ReadableByteChannel ;
3541import java .nio .file .FileVisitResult ;
3642import java .nio .file .Files ;
3743import java .nio .file .Path ;
3844import java .nio .file .Paths ;
3945import java .nio .file .SimpleFileVisitor ;
4046import java .nio .file .attribute .BasicFileAttributes ;
47+ import java .security .MessageDigest ;
48+ import java .security .NoSuchAlgorithmException ;
49+ import java .util .Locale ;
4150import java .util .zip .ZipEntry ;
4251import java .util .zip .ZipInputStream ;
4352
@@ -52,8 +61,18 @@ public class LocalGcdHelper {
5261
5362 public static final String DEFAULT_PROJECT_ID = "projectid1" ;
5463 public static final int PORT = 8080 ;
55- private static final String GCD = "gcd-head" ;
56- private static final String GCD_LOC = '/' + GCD + ".zip" ;
64+ private static final String GCD = "gcd-v1beta2-rev1-2.1.2b" ;
65+ private static final String GCD_FILENAME = GCD + ".zip" ;
66+ private static final String MD5_CHECKSUM = "d84384cdfa8658e1204f4f8be51300e8" ;
67+ private static final URL GCD_URL ;
68+
69+ static {
70+ try {
71+ GCD_URL = new URL ("http://storage.googleapis.com/gcd/tools/" + GCD_FILENAME );
72+ } catch (MalformedURLException e ) {
73+ throw new RuntimeException (e );
74+ }
75+ }
5776
5877 private static class ProcessStreamReader extends Thread {
5978
@@ -101,13 +120,32 @@ public LocalGcdHelper(String projectId) {
101120 this .projectId = projectId ;
102121 }
103122
123+ /**
124+ * Starts the local datastore for the specific project.
125+ *
126+ * This will unzip the gcd tool, create the project and start it.
127+ * All content is written to a temporary directory that will be deleted when
128+ * {@link #stop()} is called or when the program terminates) to make sure that no left-over
129+ * data from prior runs is used.
130+ */
104131 public void start () throws IOException , InterruptedException {
132+ // send a quick request in case we have a hanging process from a previous run
105133 sendQuitRequest ();
134+ // Each run is associated with its own folder that is deleted once test completes.
106135 gcdPath = Files .createTempDirectory ("gcd" );
107136 File gcdFolder = gcdPath .toFile ();
108137 gcdFolder .deleteOnExit ();
109138
110- try (ZipInputStream zipIn = new ZipInputStream (getClass ().getResourceAsStream (GCD_LOC ))) {
139+ // check if we already have a local copy of the gcd utility and download it if not.
140+ File gcdZipFile = new File (System .getProperty ("java.io.tmpdir" ), GCD_FILENAME );
141+ if (!gcdZipFile .exists () || !MD5_CHECKSUM .equals (md5 (gcdZipFile ))) {
142+ ReadableByteChannel rbc = Channels .newChannel (GCD_URL .openStream ());
143+ FileOutputStream fos = new FileOutputStream (gcdZipFile );
144+ fos .getChannel ().transferFrom (rbc , 0 , Long .MAX_VALUE );
145+ fos .close ();
146+ }
147+ // unzip the gcd
148+ try (ZipInputStream zipIn = new ZipInputStream (new FileInputStream (gcdZipFile ))) {
111149 ZipEntry entry = zipIn .getNextEntry ();
112150 while (entry != null ) {
113151 File filePath = new File (gcdFolder , entry .getName ());
@@ -120,27 +158,60 @@ public void start() throws IOException, InterruptedException {
120158 entry = zipIn .getNextEntry ();
121159 }
122160 }
123-
161+ // cleanup any possible data for the same project
124162 File datasetFolder = new File (gcdFolder , GCD + '/' + projectId );
125163 deleteRecurse (datasetFolder .toPath ());
126164
127- // TODO: if System.getProperty("os.name").startsWith("Windows") use cmd.exe /c and gcd.cmd
128- Process temp = new ProcessBuilder ()
129- .redirectErrorStream (true )
130- .directory (new File (gcdFolder , GCD ))
131- .redirectOutput (new File ("/dev/null" ))
132- .command ("bash" , "gcd.sh" , "create" , "-d" , projectId , projectId )
133- .start ();
165+ // create the datastore for the project
166+ ProcessBuilder processBuilder = new ProcessBuilder ()
167+ .redirectError (ProcessBuilder .Redirect .INHERIT )
168+ .directory (new File (gcdFolder , GCD ));
169+ if (isWindows ()) {
170+ processBuilder .command ("cmd" , "/C" , "gcd.cmd" , "create" , "-p" , projectId , projectId );
171+ processBuilder .redirectOutput (new File ("NULL:" ));
172+ } else {
173+ processBuilder .redirectOutput (new File ("/dev/null" ));
174+ processBuilder .command ("bash" , "gcd.sh" , "create" , "-p" , projectId , projectId );
175+ }
176+
177+ Process temp = processBuilder .start ();
134178 temp .waitFor ();
135179
136- temp = new ProcessBuilder ()
180+ // start the datastore for the project
181+ processBuilder = new ProcessBuilder ()
137182 .directory (new File (gcdFolder , GCD ))
138- .redirectErrorStream (true )
139- .command ("bash" , "gcd.sh" , "start" , "--testing" , "--allow_remote_shutdown" , projectId )
140- .start ();
183+ .redirectErrorStream (true );
184+ if (isWindows ()) {
185+ processBuilder .command ("cmd" , "/C" , "gcd.cmd" , "start" , "--testing" ,
186+ "--allow_remote_shutdown" , projectId );
187+ } else {
188+ processBuilder .command ("bash" , "gcd.sh" , "start" , "--testing" , "--allow_remote_shutdown" ,
189+ projectId );
190+ }
191+ temp = processBuilder .start ();
141192 processReader = ProcessStreamReader .start (temp , "Dev App Server is now running" );
142193 }
143194
195+ private static String md5 (File gcdZipFile ) throws IOException {
196+ try {
197+ MessageDigest md5 = MessageDigest .getInstance ("MD5" );
198+ try (InputStream is = new BufferedInputStream (new FileInputStream (gcdZipFile ))) {
199+ byte [] bytes = new byte [4 * 1024 * 1024 ];
200+ int len ;
201+ while ((len = is .read (bytes )) >= 0 ) {
202+ md5 .update (bytes , 0 , len );
203+ }
204+ }
205+ return String .format ("%032x" ,new BigInteger (1 , md5 .digest ()));
206+ } catch (NoSuchAlgorithmException e ) {
207+ throw new IOException (e );
208+ }
209+ }
210+
211+ private static boolean isWindows () {
212+ return System .getProperty ("os.name" ).toLowerCase (Locale .ENGLISH ).indexOf ("windows" ) > -1 ;
213+ }
214+
144215 private static void extractFile (ZipInputStream zipIn , File filePath ) throws IOException {
145216 try (BufferedOutputStream bos = new BufferedOutputStream (new FileOutputStream (filePath ))) {
146217 byte [] bytesIn = new byte [1024 ];
0 commit comments