1616
1717package com .google .gcloud .datastore .testing ;
1818
19- import static com .google .common .base .MoreObjects .firstNonNull ;
2019import static java .nio .charset .StandardCharsets .UTF_8 ;
2120
2221import com .google .common .base .Strings ;
5554import java .util .List ;
5655import java .util .Locale ;
5756import java .util .Map ;
58- import java .util .logging .Level ;
59- import java .util .logging .Logger ;
6057import java .util .regex .Pattern ;
6158import java .util .zip .ZipEntry ;
6259import java .util .zip .ZipInputStream ;
6360
61+ import java .util .logging .Level ;
62+ import java .util .logging .Logger ;
63+
6464/**
6565 * Utility to start and stop local Google Cloud Datastore process.
6666 */
6767public class LocalGcdHelper {
68+
6869 private static final Logger log = Logger .getLogger (LocalGcdHelper .class .getName ());
6970
7071 private final String projectId ;
7172 private Path gcdPath ;
72- private Process startProcess ;
7373 private ProcessStreamReader processReader ;
74- private ProcessErrorStreamReader processErrorReader ;
7574 private final int port ;
7675
7776 public static final String DEFAULT_PROJECT_ID = "projectid1" ;
@@ -180,139 +179,49 @@ private static Path executablePath(String cmd) {
180179 }
181180
182181 private static class ProcessStreamReader extends Thread {
182+
183+ private final Process process ;
183184 private final BufferedReader reader ;
184- private volatile boolean terminated ;
185185
186- ProcessStreamReader (InputStream inputStream ) {
186+ ProcessStreamReader (Process process , String blockUntil ) throws IOException {
187187 super ("Local GCD InputStream reader" );
188188 setDaemon (true );
189- reader = new BufferedReader (new InputStreamReader (inputStream ));
190- }
191-
192- void terminate () throws IOException {
193- terminated = true ;
194- reader .close ();
195- }
196-
197- @ Override
198- public void run () {
199- while (!terminated ) {
200- try {
201- String line = reader .readLine ();
202- if (line == null ) {
203- terminated = true ;
204- }
205- } catch (IOException e ) {
206- // ignore
207- }
208- }
209- }
210-
211- public static ProcessStreamReader start (InputStream inputStream ) {
212- ProcessStreamReader thread = new ProcessStreamReader (inputStream );
213- thread .start ();
214- return thread ;
215- }
216- }
217-
218- private static class ProcessErrorStreamReader extends Thread {
219- private static final int LOG_LENGTH_LIMIT = 50000 ;
220- private static final String GCD_LOGGING_CLASS =
221- "com.google.apphosting.client.serviceapp.BaseApiServlet" ;
222-
223- private final BufferedReader errorReader ;
224- private StringBuilder currentLog ;
225- private Level currentLogLevel ;
226- private boolean collectionMode ;
227- private volatile boolean terminated ;
228-
229- ProcessErrorStreamReader (InputStream errorStream , String blockUntil ) throws IOException {
230- super ("Local GCD ErrorStream reader" );
231- setDaemon (true );
232- errorReader = new BufferedReader (new InputStreamReader (errorStream ));
189+ this .process = process ;
190+ reader = new BufferedReader (new InputStreamReader (process .getInputStream ()));
233191 if (!Strings .isNullOrEmpty (blockUntil )) {
234192 String line ;
235193 do {
236- line = errorReader .readLine ();
194+ line = reader .readLine ();
237195 } while (line != null && !line .contains (blockUntil ));
238196 }
239197 }
240198
241- void terminate () throws IOException {
242- terminated = true ;
243- errorReader .close ();
199+ void terminate () throws InterruptedException , IOException {
200+ process .destroy ();
201+ process .waitFor ();
202+ reader .close ();
244203 }
245204
246205 @ Override
247206 public void run () {
248- String previousLine = "" ;
249- String nextLine = "" ;
250- while (!terminated ) {
251- try {
252- previousLine = nextLine ;
253- nextLine = errorReader .readLine ();
254- if (nextLine == null ) {
255- terminated = true ;
256- } else {
257- processLogLine (previousLine , nextLine );
258- }
259- } catch (IOException e ) {
260- // ignore
261- }
262- }
263- processLogLine (previousLine , firstNonNull (nextLine , "" ));
264- writeLog (currentLogLevel , currentLog );
265- }
266-
267- private void processLogLine (String previousLine , String nextLine ) {
268- // Each gcd log is two lines with the following format:
269- // [Date] [Time] [GCD_LOGGING_CLASS] [method]
270- // [LEVEL]: error message
271- // Exceptions and stack traces are included in gcd error stream, separated by a newline
272- Level nextLogLevel = getLevel (nextLine );
273- if (nextLogLevel != null ) {
274- writeLog (currentLogLevel , currentLog );
275- currentLog = new StringBuilder ();
276- currentLogLevel = nextLogLevel ;
277- collectionMode = previousLine .contains (GCD_LOGGING_CLASS );
278- } else if (collectionMode ) {
279- if (currentLog .length () > LOG_LENGTH_LIMIT ) {
280- collectionMode = false ;
281- } else if (currentLog .length () == 0 ) {
282- // strip level out of the line
283- currentLog .append ("GCD" );
284- currentLog .append (previousLine .split (":" , 2 )[1 ]);
285- currentLog .append (System .getProperty ("line.separator" ));
286- } else {
287- currentLog .append (previousLine );
288- currentLog .append (System .getProperty ("line.separator" ));
289- }
290- }
291- }
292-
293- private static void writeLog (Level level , StringBuilder msg ) {
294- if (level != null && msg != null && msg .length () != 0 ) {
295- log .log (level , msg .toString ().trim ());
296- }
297- }
298-
299- private static Level getLevel (String line ) {
300207 try {
301- return Level .parse (line .split (":" )[0 ]);
302- } catch (IllegalArgumentException e ) {
303- return null ; // level wasn't supplied in this log line
208+ while (reader .readLine () != null ) {
209+ // consume line
210+ }
211+ } catch (IOException e ) {
212+ // ignore
304213 }
305214 }
306215
307- public static ProcessErrorStreamReader start (InputStream errorStream , String blockUntil )
308- throws IOException {
309- ProcessErrorStreamReader thread = new ProcessErrorStreamReader (errorStream , blockUntil );
216+ public static ProcessStreamReader start (Process process , String blockUntil ) throws IOException {
217+ ProcessStreamReader thread = new ProcessStreamReader (process , blockUntil );
310218 thread .start ();
311219 return thread ;
312220 }
313221 }
314222
315223 private static class CommandWrapper {
224+
316225 private final List <String > prefix ;
317226 private List <String > command ;
318227 private String nullFilename ;
@@ -483,15 +392,13 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept
483392 if (log .isLoggable (Level .FINE )) {
484393 log .log (Level .FINE , "Starting datastore emulator for the project: {0}" , projectId );
485394 }
486- startProcess =
487- CommandWrapper .create ()
488- .command (gcdAbsolutePath .toString (), "start" , "--testing" , "--allow_remote_shutdown" ,
489- "--port=" + Integer .toString (port ), projectId )
490- .directory (gcdPath )
491- .start ();
492- processReader = ProcessStreamReader .start (startProcess .getInputStream ());
493- processErrorReader = ProcessErrorStreamReader .start (
494- startProcess .getErrorStream (), "Dev App Server is now running" );
395+ Process startProcess = CommandWrapper .create ()
396+ .command (gcdAbsolutePath .toString (), "start" , "--testing" , "--allow_remote_shutdown" ,
397+ "--port=" + Integer .toString (port ), projectId )
398+ .directory (gcdPath )
399+ .redirectErrorStream ()
400+ .start ();
401+ processReader = ProcessStreamReader .start (startProcess , "Dev App Server is now running" );
495402 }
496403
497404 private static String md5 (File gcdZipFile ) throws IOException {
@@ -547,9 +454,6 @@ public void stop() throws IOException, InterruptedException {
547454 sendQuitRequest (port );
548455 if (processReader != null ) {
549456 processReader .terminate ();
550- processErrorReader .terminate ();
551- startProcess .destroy ();
552- startProcess .waitFor ();
553457 }
554458 if (gcdPath != null ) {
555459 deleteRecurse (gcdPath );
@@ -561,6 +465,7 @@ private static void deleteRecurse(Path path) throws IOException {
561465 return ;
562466 }
563467 Files .walkFileTree (path , new SimpleFileVisitor <Path >() {
468+
564469 @ Override
565470 public FileVisitResult postVisitDirectory (Path dir , IOException exc ) throws IOException {
566471 Files .delete (dir );
@@ -575,7 +480,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
575480 });
576481 }
577482
578- public static LocalGcdHelper start (String projectId , int port )
483+ public static LocalGcdHelper start (String projectId , int port )
579484 throws IOException , InterruptedException {
580485 LocalGcdHelper helper = new LocalGcdHelper (projectId , port );
581486 helper .start ();
@@ -585,14 +490,15 @@ public static LocalGcdHelper start(String projectId, int port)
585490 public static void main (String ... args ) throws IOException , InterruptedException {
586491 Map <String , String > parsedArgs = parseArgs (args );
587492 String action = parsedArgs .get ("action" );
588- int port =
589- ( parsedArgs . get ( "port" ) == null ) ? DEFAULT_PORT : Integer .parseInt (parsedArgs .get ("port" ));
493+ int port = ( parsedArgs . get ( "port" ) == null ) ? DEFAULT_PORT
494+ : Integer .parseInt (parsedArgs .get ("port" ));
590495 switch (action ) {
591496 case "START" :
592497 if (!isActive (DEFAULT_PROJECT_ID , port )) {
593498 LocalGcdHelper helper = start (DEFAULT_PROJECT_ID , port );
594499 try (FileWriter writer = new FileWriter (".local_gcd_helper" )) {
595- writer .write (helper .gcdPath .toAbsolutePath ().toString () + System .lineSeparator ());
500+ writer .write (
501+ helper .gcdPath .toAbsolutePath ().toString () + System .lineSeparator ());
596502 writer .write (Integer .toString (port ));
597503 }
598504 }
0 commit comments