From 47632ab5bed28e8904b75b411c0a27402b682df8 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 15 Sep 2015 13:06:26 +0200 Subject: [PATCH 1/5] Avoid downloading gcd if gcloud is installed and gcd version matches v1beta2 --- .../gcloud/datastore/LocalGcdHelper.java | 121 +++++++++++++++--- 1 file changed, 105 insertions(+), 16 deletions(-) diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java index 2d7f5802f247..a826578104e7 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java @@ -40,6 +40,7 @@ import java.nio.channels.ReadableByteChannel; import java.nio.file.FileVisitResult; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; @@ -47,6 +48,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Locale; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -62,17 +64,85 @@ public class LocalGcdHelper { public static final String DEFAULT_PROJECT_ID = "projectid1"; public static final int PORT = 8080; private static final String GCD = "gcd-v1beta2-rev1-2.1.2b"; + private static final String GCD_VERSION = "v1beta2"; private static final String GCD_FILENAME = GCD + ".zip"; private static final String MD5_CHECKSUM = "d84384cdfa8658e1204f4f8be51300e8"; private static final URL GCD_URL; + private static final String GCLOUD = "gcloud"; + private static final Path INSTALLED_GCD_PATH; static { - try { - GCD_URL = new URL("http://storage.googleapis.com/gcd/tools/" + GCD_FILENAME); - } catch (MalformedURLException e) { - throw new RuntimeException(e); + INSTALLED_GCD_PATH = installedGcdPath(); + if (INSTALLED_GCD_PATH != null) { + GCD_URL = null; + } else { + try { + GCD_URL = new URL("http://storage.googleapis.com/gcd/tools/" + GCD_FILENAME); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } } } + + private static Path installedGcdPath() { + Path gcloudPath = executablePath(GCLOUD); + gcloudPath = (gcloudPath == null) ? null : gcloudPath.getParent(); + if (gcloudPath == null) { + return null; + } + Path installedGcdPath = gcloudPath.resolve("platform").resolve("gcd"); + if (Files.exists(installedGcdPath)) { + try { + String installedVersion = installedGcdVersion(); + if (installedVersion != null && installedVersion.startsWith(GCD_VERSION)) { + return installedGcdPath; + } + } catch (IOException | InterruptedException ignore) { + // ignore + } + } + return null; + } + + private static String installedGcdVersion() throws IOException, InterruptedException { + ProcessBuilder processBuilder = new ProcessBuilder() + .redirectErrorStream(true); + if (isWindows()) { + processBuilder.command("cmd", "/C", "gcloud", "version"); + } else { + processBuilder.command("bash", "gcloud", "version"); + } + Process process = processBuilder.start(); + process.waitFor(); + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(process.getInputStream()))) { + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + if (line.startsWith("gcd-emulator")) { + String[] lineComponents = line.split(" "); + if (lineComponents.length > 1) { + return lineComponents[1]; + } + } + } + return null; + } + + } + + private static Path executablePath(String cmd) { + String[] paths = System.getenv("PATH").split(Pattern.quote(File.pathSeparator)); + for (String pathString : paths) { + try { + Path path = Paths.get(pathString); + if (Files.exists(path.resolve(cmd))) { + return path; + } + } catch (InvalidPathException ignore) { + // ignore + } + } + return null; + } private static class ProcessStreamReader extends Thread { @@ -136,6 +206,18 @@ public void start() throws IOException, InterruptedException { File gcdFolder = gcdPath.toFile(); gcdFolder.deleteOnExit(); + Path gcdExecutablePath; + // If cloud is available we use it, otherwise we download and start gcd + if (INSTALLED_GCD_PATH == null) { + downloadGcd(); + gcdExecutablePath = gcdPath.resolve(GCD); + } else { + gcdExecutablePath = INSTALLED_GCD_PATH; + } + startGcd(gcdExecutablePath); + } + + private void downloadGcd() throws IOException { // check if we already have a local copy of the gcd utility and download it if not. File gcdZipFile = new File(System.getProperty("java.io.tmpdir"), GCD_FILENAME); if (!gcdZipFile.exists() || !MD5_CHECKSUM.equals(md5(gcdZipFile))) { @@ -148,7 +230,7 @@ public void start() throws IOException, InterruptedException { try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(gcdZipFile))) { ZipEntry entry = zipIn.getNextEntry(); while (entry != null) { - File filePath = new File(gcdFolder, entry.getName()); + File filePath = new File(gcdPath.toFile(), entry.getName()); if (!entry.isDirectory()) { extractFile(zipIn, filePath); } else { @@ -157,21 +239,26 @@ public void start() throws IOException, InterruptedException { zipIn.closeEntry(); entry = zipIn.getNextEntry(); } - } + } + } + + private void startGcd(Path executablePath) throws IOException, InterruptedException { // cleanup any possible data for the same project - File datasetFolder = new File(gcdFolder, GCD + '/' + projectId); + File datasetFolder = new File(gcdPath.toFile(), projectId); deleteRecurse(datasetFolder.toPath()); // create the datastore for the project ProcessBuilder processBuilder = new ProcessBuilder() .redirectError(ProcessBuilder.Redirect.INHERIT) - .directory(new File(gcdFolder, GCD)); + .directory(gcdPath.toFile()); if (isWindows()) { - processBuilder.command("cmd", "/C", "gcd.cmd", "create", "-p", projectId, projectId); - processBuilder.redirectOutput(new File("NULL:")); + Path gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.cmd"); + processBuilder.command("cmd", "/C", gcdAbsolutePath.toString(), "create", + "-p", projectId, projectId); } else { - processBuilder.redirectOutput(new File("/dev/null")); - processBuilder.command("bash", "gcd.sh", "create", "-p", projectId, projectId); + Path gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.sh"); + processBuilder.command("bash", gcdAbsolutePath.toString(), "create", + "-p", projectId, projectId); } Process temp = processBuilder.start(); @@ -179,14 +266,16 @@ public void start() throws IOException, InterruptedException { // start the datastore for the project processBuilder = new ProcessBuilder() - .directory(new File(gcdFolder, GCD)) + .directory(gcdPath.toFile()) .redirectErrorStream(true); if (isWindows()) { - processBuilder.command("cmd", "/C", "gcd.cmd", "start", "--testing", + Path gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.cmd"); + processBuilder.command("cmd", "/C", gcdAbsolutePath.toString(), "start", "--testing", "--allow_remote_shutdown", projectId); } else { - processBuilder.command("bash", "gcd.sh", "start", "--testing", "--allow_remote_shutdown", - projectId); + Path gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.sh"); + processBuilder.command("bash", gcdAbsolutePath.toString(), "start", "--testing", + "--allow_remote_shutdown", projectId); } temp = processBuilder.start(); processReader = ProcessStreamReader.start(temp, "Dev App Server is now running"); From 1c52730edfb6e169ea9ce8af32713f4879f07590 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 17 Sep 2015 14:10:42 +0200 Subject: [PATCH 2/5] Refactor process invocation for gcd in tests. Restored Windows support. --- .../gcloud/datastore/LocalGcdHelper.java | 160 ++++++++++++++---- 1 file changed, 123 insertions(+), 37 deletions(-) diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java index a826578104e7..2392482f9fd8 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java @@ -47,6 +47,9 @@ import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Locale; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -56,20 +59,22 @@ * Utility to start and stop local Google Cloud Datastore process. */ public class LocalGcdHelper { - + private final String projectId; private Path gcdPath; private ProcessStreamReader processReader; public static final String DEFAULT_PROJECT_ID = "projectid1"; public static final int PORT = 8080; - private static final String GCD = "gcd-v1beta2-rev1-2.1.2b"; private static final String GCD_VERSION = "v1beta2"; - private static final String GCD_FILENAME = GCD + ".zip"; + private static final String GCD_BUILD = "rev1-2.1.2b"; + private static final String GCD_BASENAME = "gcd-" + GCD_VERSION + "-" + GCD_BUILD; + private static final String GCD_FILENAME = GCD_BASENAME + ".zip"; private static final String MD5_CHECKSUM = "d84384cdfa8658e1204f4f8be51300e8"; private static final URL GCD_URL; private static final String GCLOUD = "gcloud"; private static final Path INSTALLED_GCD_PATH; + private static final String GCD_VERSION_PREFIX = "gcd-emulator "; static { INSTALLED_GCD_PATH = installedGcdPath(); @@ -85,7 +90,13 @@ public class LocalGcdHelper { } private static Path installedGcdPath() { - Path gcloudPath = executablePath(GCLOUD); + String gcloudExecutableName; + if (isWindows()) { + gcloudExecutableName = GCLOUD + ".cmd"; + } else { + gcloudExecutableName = GCLOUD; + } + Path gcloudPath = executablePath(gcloudExecutableName); gcloudPath = (gcloudPath == null) ? null : gcloudPath.getParent(); if (gcloudPath == null) { return null; @@ -105,19 +116,18 @@ private static Path installedGcdPath() { } private static String installedGcdVersion() throws IOException, InterruptedException { - ProcessBuilder processBuilder = new ProcessBuilder() - .redirectErrorStream(true); + CommandWrapper gcloudCommand; if (isWindows()) { - processBuilder.command("cmd", "/C", "gcloud", "version"); + gcloudCommand = CommandWrapper.cmd(); } else { - processBuilder.command("bash", "gcloud", "version"); + gcloudCommand = CommandWrapper.bash(); } - Process process = processBuilder.start(); + Process process = gcloudCommand.command("gcloud", "version").redirectErrorStream().start(); process.waitFor(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { for (String line = reader.readLine(); line != null; line = reader.readLine()) { - if (line.startsWith("gcd-emulator")) { + if (line.startsWith(GCD_VERSION_PREFIX)) { String[] lineComponents = line.split(" "); if (lineComponents.length > 1) { return lineComponents[1]; @@ -126,7 +136,6 @@ private static String installedGcdVersion() throws IOException, InterruptedExcep } return null; } - } private static Path executablePath(String cmd) { @@ -185,6 +194,84 @@ public static ProcessStreamReader start(Process process, String blockUntil) thro return thread; } } + + private static class CommandWrapper { + + private final List prefix; + private List command; + private String nullFilename; + private boolean redirectOutputToNull; + private boolean redirectErrorStream; + private boolean redirectErrorInherit; + private Path directory; + + private CommandWrapper() { + this.prefix = new ArrayList<>(); + } + + public CommandWrapper command(String... command) { + this.command = new ArrayList<>(command.length + this.prefix.size()); + this.command.addAll(prefix); + this.command.addAll(Arrays.asList(command)); + return this; + } + + public CommandWrapper redirectOutputToNull() { + this.redirectOutputToNull = true; + return this; + } + + public CommandWrapper redirectErrorStream() { + this.redirectErrorStream = true; + return this; + } + + public CommandWrapper redirectErrorInherit() { + this.redirectErrorInherit = true; + return this; + } + + public CommandWrapper directory(Path directory) { + this.directory = directory; + return this; + } + + public ProcessBuilder builder() { + ProcessBuilder builder = new ProcessBuilder(command); + if (redirectOutputToNull) { + builder.redirectOutput(new File(nullFilename)); + } + if (directory != null) { + builder.directory(directory.toFile()); + } + if (redirectErrorStream) { + builder.redirectErrorStream(true); + } + if (redirectErrorInherit) { + builder.redirectError(ProcessBuilder.Redirect.INHERIT); + } + return builder; + } + + public Process start() throws IOException { + return builder().start(); + } + + public static CommandWrapper cmd() { + CommandWrapper wrapper = new CommandWrapper(); + wrapper.prefix.add("cmd"); + wrapper.prefix.add("/C"); + wrapper.nullFilename = "NUL:"; + return wrapper; + } + + public static CommandWrapper bash() { + CommandWrapper wrapper = new CommandWrapper(); + wrapper.prefix.add("bash"); + wrapper.nullFilename = "/dev/null"; + return wrapper; + } + } public LocalGcdHelper(String projectId) { this.projectId = projectId; @@ -210,7 +297,7 @@ public void start() throws IOException, InterruptedException { // If cloud is available we use it, otherwise we download and start gcd if (INSTALLED_GCD_PATH == null) { downloadGcd(); - gcdExecutablePath = gcdPath.resolve(GCD); + gcdExecutablePath = gcdPath.resolve(GCD_BASENAME); } else { gcdExecutablePath = INSTALLED_GCD_PATH; } @@ -226,6 +313,7 @@ private void downloadGcd() throws IOException { fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); } } + // unzip the gcd try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(gcdZipFile))) { ZipEntry entry = zipIn.getNextEntry(); @@ -246,38 +334,36 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept // cleanup any possible data for the same project File datasetFolder = new File(gcdPath.toFile(), projectId); deleteRecurse(datasetFolder.toPath()); - - // create the datastore for the project - ProcessBuilder processBuilder = new ProcessBuilder() - .redirectError(ProcessBuilder.Redirect.INHERIT) - .directory(gcdPath.toFile()); + + // create command wrappers + CommandWrapper gcdCreateCommand; + CommandWrapper gcdStartCommand; + Path gcdAbsolutePath; if (isWindows()) { - Path gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.cmd"); - processBuilder.command("cmd", "/C", gcdAbsolutePath.toString(), "create", - "-p", projectId, projectId); + gcdCreateCommand = CommandWrapper.cmd(); + gcdStartCommand = CommandWrapper.cmd(); + gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.cmd"); } else { - Path gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.sh"); - processBuilder.command("bash", gcdAbsolutePath.toString(), "create", - "-p", projectId, projectId); + gcdCreateCommand = CommandWrapper.bash(); + gcdStartCommand = CommandWrapper.bash(); + gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.sh"); } - Process temp = processBuilder.start(); + // create the datastore for the project + Process temp = gcdCreateCommand.command(gcdAbsolutePath.toString(), "create", "-p", projectId, + projectId) + .redirectErrorInherit() + .directory(gcdPath) + .redirectOutputToNull() + .start(); temp.waitFor(); // start the datastore for the project - processBuilder = new ProcessBuilder() - .directory(gcdPath.toFile()) - .redirectErrorStream(true); - if (isWindows()) { - Path gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.cmd"); - processBuilder.command("cmd", "/C", gcdAbsolutePath.toString(), "start", "--testing", - "--allow_remote_shutdown", projectId); - } else { - Path gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.sh"); - processBuilder.command("bash", gcdAbsolutePath.toString(), "start", "--testing", - "--allow_remote_shutdown", projectId); - } - temp = processBuilder.start(); + temp = gcdStartCommand.command(gcdAbsolutePath.toString(), "start", "--testing", + "--allow_remote_shutdown", projectId) + .directory(gcdPath) + .redirectErrorStream() + .start(); processReader = ProcessStreamReader.start(temp, "Dev App Server is now running"); } From b0a88f5d512908159d5c7316f363d4a1afb97658 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 17 Sep 2015 14:32:34 +0200 Subject: [PATCH 3/5] Add fine-grained logging to LocalGcdHelper --- .../gcloud/datastore/LocalGcdHelper.java | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java index 2392482f9fd8..d0648da46be2 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java @@ -55,11 +55,16 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * Utility to start and stop local Google Cloud Datastore process. */ public class LocalGcdHelper { + private static final Logger log = Logger.getLogger(LocalGcdHelper.class.getName()); + private final String projectId; private Path gcdPath; private ProcessStreamReader processReader; @@ -99,14 +104,27 @@ private static Path installedGcdPath() { Path gcloudPath = executablePath(gcloudExecutableName); gcloudPath = (gcloudPath == null) ? null : gcloudPath.getParent(); if (gcloudPath == null) { + if (log.isLoggable(Level.FINE)) { + log.fine("SDK not found"); + } return null; } + if (log.isLoggable(Level.FINE)) { + log.fine("SDK found, looking for datastore emulator"); + } Path installedGcdPath = gcloudPath.resolve("platform").resolve("gcd"); if (Files.exists(installedGcdPath)) { try { String installedVersion = installedGcdVersion(); if (installedVersion != null && installedVersion.startsWith(GCD_VERSION)) { + if (log.isLoggable(Level.FINE)) { + log.fine("SDK datastore emulator found"); + } return installedGcdPath; + } else { + if (log.isLoggable(Level.FINE)) { + log.fine("SDK datastore emulator found but version mismatch"); + } } } catch (IOException | InterruptedException ignore) { // ignore @@ -308,14 +326,23 @@ private void downloadGcd() throws IOException { // check if we already have a local copy of the gcd utility and download it if not. File gcdZipFile = new File(System.getProperty("java.io.tmpdir"), GCD_FILENAME); if (!gcdZipFile.exists() || !MD5_CHECKSUM.equals(md5(gcdZipFile))) { + if (log.isLoggable(Level.FINE)) { + log.fine("Fetching datastore emulator"); + } ReadableByteChannel rbc = Channels.newChannel(GCD_URL.openStream()); try (FileOutputStream fos = new FileOutputStream(gcdZipFile)) { fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); } + } else { + if (log.isLoggable(Level.FINE)) { + log.fine("Using cached datastore emulator"); + } } - // unzip the gcd try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(gcdZipFile))) { + if (log.isLoggable(Level.FINE)) { + log.fine("Unzipping datastore emulator"); + } ZipEntry entry = zipIn.getNextEntry(); while (entry != null) { File filePath = new File(gcdPath.toFile(), entry.getName()); @@ -350,6 +377,9 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept } // create the datastore for the project + if (log.isLoggable(Level.FINE)) { + log.log(Level.FINE, "Creating datastore for the project: {0}", projectId); + } Process temp = gcdCreateCommand.command(gcdAbsolutePath.toString(), "create", "-p", projectId, projectId) .redirectErrorInherit() @@ -359,6 +389,9 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept temp.waitFor(); // start the datastore for the project + if (log.isLoggable(Level.FINE)) { + log.log(Level.FINE, "Starting datastore emulator for the project: {0}", projectId); + } temp = gcdStartCommand.command(gcdAbsolutePath.toString(), "start", "--testing", "--allow_remote_shutdown", projectId) .directory(gcdPath) From e792c2c67795572501a2bd2cc04cb7a183a41a3f Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Fri, 18 Sep 2015 09:47:57 +0200 Subject: [PATCH 4/5] Formatted LocalGcdHelper --- .../gcloud/datastore/LocalGcdHelper.java | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java index d0648da46be2..20b44f3280d1 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java @@ -62,7 +62,7 @@ * Utility to start and stop local Google Cloud Datastore process. */ public class LocalGcdHelper { - + private static final Logger log = Logger.getLogger(LocalGcdHelper.class.getName()); private final String projectId; @@ -93,7 +93,7 @@ public class LocalGcdHelper { } } } - + private static Path installedGcdPath() { String gcloudExecutableName; if (isWindows()) { @@ -132,7 +132,7 @@ private static Path installedGcdPath() { } return null; } - + private static String installedGcdVersion() throws IOException, InterruptedException { CommandWrapper gcloudCommand; if (isWindows()) { @@ -142,8 +142,8 @@ private static String installedGcdVersion() throws IOException, InterruptedExcep } Process process = gcloudCommand.command("gcloud", "version").redirectErrorStream().start(); process.waitFor(); - try (BufferedReader reader = - new BufferedReader(new InputStreamReader(process.getInputStream()))) { + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(process.getInputStream()))) { for (String line = reader.readLine(); line != null; line = reader.readLine()) { if (line.startsWith(GCD_VERSION_PREFIX)) { String[] lineComponents = line.split(" "); @@ -155,7 +155,7 @@ private static String installedGcdVersion() throws IOException, InterruptedExcep return null; } } - + private static Path executablePath(String cmd) { String[] paths = System.getenv("PATH").split(Pattern.quote(File.pathSeparator)); for (String pathString : paths) { @@ -180,7 +180,7 @@ private static class ProcessStreamReader extends Thread { super("Local GCD InputStream reader"); setDaemon(true); this.process = process; - reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + reader = new BufferedReader(new InputStreamReader(process.getInputStream())); if (!Strings.isNullOrEmpty(blockUntil)) { String line; do { @@ -212,9 +212,9 @@ public static ProcessStreamReader start(Process process, String blockUntil) thro return thread; } } - + private static class CommandWrapper { - + private final List prefix; private List command; private String nullFilename; @@ -222,38 +222,38 @@ private static class CommandWrapper { private boolean redirectErrorStream; private boolean redirectErrorInherit; private Path directory; - + private CommandWrapper() { this.prefix = new ArrayList<>(); } - + public CommandWrapper command(String... command) { this.command = new ArrayList<>(command.length + this.prefix.size()); this.command.addAll(prefix); this.command.addAll(Arrays.asList(command)); return this; } - + public CommandWrapper redirectOutputToNull() { this.redirectOutputToNull = true; return this; } - + public CommandWrapper redirectErrorStream() { this.redirectErrorStream = true; return this; } - + public CommandWrapper redirectErrorInherit() { this.redirectErrorInherit = true; return this; } - + public CommandWrapper directory(Path directory) { this.directory = directory; return this; } - + public ProcessBuilder builder() { ProcessBuilder builder = new ProcessBuilder(command); if (redirectOutputToNull) { @@ -270,11 +270,11 @@ public ProcessBuilder builder() { } return builder; } - + public Process start() throws IOException { return builder().start(); } - + public static CommandWrapper cmd() { CommandWrapper wrapper = new CommandWrapper(); wrapper.prefix.add("cmd"); @@ -282,7 +282,7 @@ public static CommandWrapper cmd() { wrapper.nullFilename = "NUL:"; return wrapper; } - + public static CommandWrapper bash() { CommandWrapper wrapper = new CommandWrapper(); wrapper.prefix.add("bash"); @@ -321,7 +321,7 @@ public void start() throws IOException, InterruptedException { } startGcd(gcdExecutablePath); } - + private void downloadGcd() throws IOException { // check if we already have a local copy of the gcd utility and download it if not. File gcdZipFile = new File(System.getProperty("java.io.tmpdir"), GCD_FILENAME); @@ -336,7 +336,7 @@ private void downloadGcd() throws IOException { } else { if (log.isLoggable(Level.FINE)) { log.fine("Using cached datastore emulator"); - } + } } // unzip the gcd try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(gcdZipFile))) { @@ -354,14 +354,14 @@ private void downloadGcd() throws IOException { zipIn.closeEntry(); entry = zipIn.getNextEntry(); } - } + } } - + private void startGcd(Path executablePath) throws IOException, InterruptedException { // cleanup any possible data for the same project File datasetFolder = new File(gcdPath.toFile(), projectId); deleteRecurse(datasetFolder.toPath()); - + // create command wrappers CommandWrapper gcdCreateCommand; CommandWrapper gcdStartCommand; @@ -380,24 +380,26 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "Creating datastore for the project: {0}", projectId); } - Process temp = gcdCreateCommand.command(gcdAbsolutePath.toString(), "create", "-p", projectId, - projectId) + Process createProcess = + gcdCreateCommand.command(gcdAbsolutePath.toString(), "create", "-p", projectId, projectId) .redirectErrorInherit() .directory(gcdPath) .redirectOutputToNull() .start(); - temp.waitFor(); + createProcess.waitFor(); // start the datastore for the project if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "Starting datastore emulator for the project: {0}", projectId); } - temp = gcdStartCommand.command(gcdAbsolutePath.toString(), "start", "--testing", - "--allow_remote_shutdown", projectId) + Process startProcess = + gcdStartCommand + .command(gcdAbsolutePath.toString(), "start", "--testing", "--allow_remote_shutdown", + projectId) .directory(gcdPath) .redirectErrorStream() .start(); - processReader = ProcessStreamReader.start(temp, "Dev App Server is now running"); + processReader = ProcessStreamReader.start(startProcess, "Dev App Server is now running"); } private static String md5(File gcdZipFile) throws IOException { @@ -410,7 +412,7 @@ private static String md5(File gcdZipFile) throws IOException { md5.update(bytes, 0, len); } } - return String.format("%032x",new BigInteger(1, md5.digest())); + return String.format("%032x", new BigInteger(1, md5.digest())); } catch (NoSuchAlgorithmException e) { throw new IOException(e); } @@ -520,7 +522,7 @@ public static boolean isActive(String projectId) { urlBuilder.append("/datastore/v1beta2/datasets/").append(projectId).append("/lookup"); URL url = new URL(urlBuilder.toString()); try (BufferedReader reader = - new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) { + new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) { return "Valid RPC".equals(reader.readLine()); } } catch (IOException ignore) { From f753b44d9a2f2b787b4e9fc1f51b67f47a19ba68 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Fri, 18 Sep 2015 10:11:17 +0200 Subject: [PATCH 5/5] Single factory method for CommandWrapper in LocalGcdHelper --- .../gcloud/datastore/LocalGcdHelper.java | 45 +++++++------------ 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java index 20b44f3280d1..888fd2157d3a 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java @@ -134,13 +134,8 @@ private static Path installedGcdPath() { } private static String installedGcdVersion() throws IOException, InterruptedException { - CommandWrapper gcloudCommand; - if (isWindows()) { - gcloudCommand = CommandWrapper.cmd(); - } else { - gcloudCommand = CommandWrapper.bash(); - } - Process process = gcloudCommand.command("gcloud", "version").redirectErrorStream().start(); + Process process = + CommandWrapper.create().command("gcloud", "version").redirectErrorStream().start(); process.waitFor(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { @@ -225,6 +220,14 @@ private static class CommandWrapper { private CommandWrapper() { this.prefix = new ArrayList<>(); + if (isWindows()) { + this.prefix.add("cmd"); + this.prefix.add("/C"); + this.nullFilename = "NUL:"; + } else { + this.prefix.add("bash"); + this.nullFilename = "/dev/null"; + } } public CommandWrapper command(String... command) { @@ -275,19 +278,8 @@ public Process start() throws IOException { return builder().start(); } - public static CommandWrapper cmd() { - CommandWrapper wrapper = new CommandWrapper(); - wrapper.prefix.add("cmd"); - wrapper.prefix.add("/C"); - wrapper.nullFilename = "NUL:"; - return wrapper; - } - - public static CommandWrapper bash() { - CommandWrapper wrapper = new CommandWrapper(); - wrapper.prefix.add("bash"); - wrapper.nullFilename = "/dev/null"; - return wrapper; + public static CommandWrapper create() { + return new CommandWrapper(); } } @@ -362,17 +354,11 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept File datasetFolder = new File(gcdPath.toFile(), projectId); deleteRecurse(datasetFolder.toPath()); - // create command wrappers - CommandWrapper gcdCreateCommand; - CommandWrapper gcdStartCommand; + // Get path to cmd executable Path gcdAbsolutePath; if (isWindows()) { - gcdCreateCommand = CommandWrapper.cmd(); - gcdStartCommand = CommandWrapper.cmd(); gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.cmd"); } else { - gcdCreateCommand = CommandWrapper.bash(); - gcdStartCommand = CommandWrapper.bash(); gcdAbsolutePath = executablePath.toAbsolutePath().resolve("gcd.sh"); } @@ -381,7 +367,8 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept log.log(Level.FINE, "Creating datastore for the project: {0}", projectId); } Process createProcess = - gcdCreateCommand.command(gcdAbsolutePath.toString(), "create", "-p", projectId, projectId) + CommandWrapper.create() + .command(gcdAbsolutePath.toString(), "create", "-p", projectId, projectId) .redirectErrorInherit() .directory(gcdPath) .redirectOutputToNull() @@ -393,7 +380,7 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept log.log(Level.FINE, "Starting datastore emulator for the project: {0}", projectId); } Process startProcess = - gcdStartCommand + CommandWrapper.create() .command(gcdAbsolutePath.toString(), "start", "--testing", "--allow_remote_shutdown", projectId) .directory(gcdPath)