diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java index e7dc71c50ff6..b3365876773e 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java @@ -34,6 +34,7 @@ public class DatastoreOptionsTest { private static final String PROJECT_ID = "project_id"; + private static final int PORT = LocalGcdHelper.findAvailablePort(LocalGcdHelper.DEFAULT_PORT); private DatastoreRpcFactory datastoreRpcFactory; private DatastoreRpc datastoreRpc; private DatastoreOptions.Builder options; @@ -46,7 +47,7 @@ public void setUp() throws IOException, InterruptedException { .normalizeDataset(false) .serviceRpcFactory(datastoreRpcFactory) .projectId(PROJECT_ID) - .host("http://localhost:" + LocalGcdHelper.PORT); + .host("http://localhost:" + PORT); EasyMock.expect(datastoreRpcFactory.create(EasyMock.anyObject(DatastoreOptions.class))) .andReturn(datastoreRpc) .anyTimes(); @@ -60,7 +61,7 @@ public void testProjectId() throws Exception { @Test public void testHost() throws Exception { - assertEquals("http://localhost:" + LocalGcdHelper.PORT, options.build().host()); + assertEquals("http://localhost:" + PORT, options.build().host()); } @Test diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java index 156f9684f8ba..b03472a9a6f8 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java @@ -101,11 +101,12 @@ public class DatastoreTest { private Datastore datastore; private static LocalGcdHelper gcdHelper; + private static final int PORT = LocalGcdHelper.findAvailablePort(LocalGcdHelper.DEFAULT_PORT); @BeforeClass public static void beforeClass() throws IOException, InterruptedException { - if (!LocalGcdHelper.isActive(PROJECT_ID)) { - gcdHelper = LocalGcdHelper.start(PROJECT_ID); + if (!LocalGcdHelper.isActive(PROJECT_ID, PORT)) { + gcdHelper = LocalGcdHelper.start(PROJECT_ID, PORT); } } @@ -113,7 +114,7 @@ public static void beforeClass() throws IOException, InterruptedException { public void setUp() throws IOException, InterruptedException { options = DatastoreOptions.builder() .projectId(PROJECT_ID) - .host("http://localhost:" + LocalGcdHelper.PORT) + .host("http://localhost:" + PORT) .build(); datastore = DatastoreFactory.instance().get(options); StructuredQuery query = Query.keyQueryBuilder().build(); 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 888fd2157d3a..0e0e726a7fba 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 @@ -35,6 +35,7 @@ import java.math.BigInteger; import java.net.HttpURLConnection; import java.net.MalformedURLException; +import java.net.ServerSocket; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; @@ -49,8 +50,10 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -68,9 +71,10 @@ public class LocalGcdHelper { private final String projectId; private Path gcdPath; private ProcessStreamReader processReader; + private final int port; public static final String DEFAULT_PROJECT_ID = "projectid1"; - public static final int PORT = 8080; + public static final int DEFAULT_PORT = 8080; private static final String GCD_VERSION = "v1beta2"; private static final String GCD_BUILD = "rev1-2.1.2b"; private static final String GCD_BASENAME = "gcd-" + GCD_VERSION + "-" + GCD_BUILD; @@ -94,6 +98,14 @@ public class LocalGcdHelper { } } + public static int findAvailablePort(int defaultPort) { + try (ServerSocket tempSocket = new ServerSocket(0)) { + return tempSocket.getLocalPort(); + } catch (IOException e) { + return defaultPort; + } + } + private static Path installedGcdPath() { String gcloudExecutableName; if (isWindows()) { @@ -283,8 +295,9 @@ public static CommandWrapper create() { } } - public LocalGcdHelper(String projectId) { + public LocalGcdHelper(String projectId, int port) { this.projectId = projectId; + this.port = port; } /** @@ -297,7 +310,7 @@ public LocalGcdHelper(String projectId) { */ public void start() throws IOException, InterruptedException { // send a quick request in case we have a hanging process from a previous run - sendQuitRequest(); + sendQuitRequest(port); // Each run is associated with its own folder that is deleted once test completes. gcdPath = Files.createTempDirectory("gcd"); File gcdFolder = gcdPath.toFile(); @@ -379,13 +392,12 @@ private void startGcd(Path executablePath) throws IOException, InterruptedExcept if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "Starting datastore emulator for the project: {0}", projectId); } - Process startProcess = - CommandWrapper.create() - .command(gcdAbsolutePath.toString(), "start", "--testing", "--allow_remote_shutdown", - projectId) - .directory(gcdPath) - .redirectErrorStream() - .start(); + Process startProcess = CommandWrapper.create() + .command(gcdAbsolutePath.toString(), "start", "--testing", "--allow_remote_shutdown", + "--port=" + Integer.toString(port), projectId) + .directory(gcdPath) + .redirectErrorStream() + .start(); processReader = ProcessStreamReader.start(startProcess, "Dev App Server is now running"); } @@ -419,9 +431,9 @@ private static void extractFile(ZipInputStream zipIn, File filePath) throws IOEx } } - public static void sendQuitRequest() { + public static void sendQuitRequest(int port) { try { - URL url = new URL("http", "localhost", PORT, "/_ah/admin/quit"); + URL url = new URL("http", "localhost", port, "/_ah/admin/quit"); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("POST"); con.setDoOutput(true); @@ -439,7 +451,7 @@ public static void sendQuitRequest() { } public void stop() throws IOException, InterruptedException { - sendQuitRequest(); + sendQuitRequest(port); if (processReader != null) { processReader.terminate(); } @@ -468,44 +480,70 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO }); } - public static LocalGcdHelper start(String projectId) throws IOException, InterruptedException { - LocalGcdHelper helper = new LocalGcdHelper(projectId); + public static LocalGcdHelper start(String projectId, int port) + throws IOException, InterruptedException { + LocalGcdHelper helper = new LocalGcdHelper(projectId, port); helper.start(); return helper; } public static void main(String... args) throws IOException, InterruptedException { - if (args.length == 1) { - switch (args[0]) { - case "START": - if (!isActive(DEFAULT_PROJECT_ID)) { - LocalGcdHelper helper = start(DEFAULT_PROJECT_ID); - try (FileWriter writer = new FileWriter(".local_gcd_helper")) { - writer.write(helper.gcdPath.toAbsolutePath().toString()); - } + Map parsedArgs = parseArgs(args); + String action = parsedArgs.get("action"); + int port = (parsedArgs.get("port") == null) ? DEFAULT_PORT + : Integer.parseInt(parsedArgs.get("port")); + switch (action) { + case "START": + if (!isActive(DEFAULT_PROJECT_ID, port)) { + LocalGcdHelper helper = start(DEFAULT_PROJECT_ID, port); + try (FileWriter writer = new FileWriter(".local_gcd_helper")) { + writer.write( + helper.gcdPath.toAbsolutePath().toString() + System.lineSeparator()); + writer.write(Integer.toString(port)); } - return; - case "STOP": - sendQuitRequest(); - File file = new File(".local_gcd_helper"); - if (file.exists()) { - try (BufferedReader reader = new BufferedReader(new FileReader(file))) { - String path = reader.readLine(); - deleteRecurse(Paths.get(path)); - } - file.delete(); + } + return; + case "STOP": + File file = new File(".local_gcd_helper"); + String path = null; + boolean fileExists = file.exists(); + if (fileExists) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + path = reader.readLine(); + port = Integer.parseInt(reader.readLine()); } - return; - default: - break; + } + sendQuitRequest(port); + if (fileExists) { + deleteRecurse(Paths.get(path)); + file.delete(); + } + return; + default: + break; + } + } + + private static Map parseArgs(String[] args) { + Map parsedArgs = new HashMap(); + for (String arg : args) { + if (arg.startsWith("--port=")) { + parsedArgs.put("port", arg.substring("--port=".length())); + } else if (arg.equals("START") || arg.equals("STOP")) { + parsedArgs.put("action", arg); + } else { + throw new RuntimeException("Only accepts START, STOP, and --port= as arguments"); } } - throw new RuntimeException("expecting only START | STOP"); + if (parsedArgs.get("action") == null) { + throw new RuntimeException("EXPECTING START | STOP"); + } + return parsedArgs; } - public static boolean isActive(String projectId) { + public static boolean isActive(String projectId, int port) { try { - StringBuilder urlBuilder = new StringBuilder("http://localhost:").append(PORT); + StringBuilder urlBuilder = new StringBuilder("http://localhost:").append(port); urlBuilder.append("/datastore/v1beta2/datasets/").append(projectId).append("/lookup"); URL url = new URL(urlBuilder.toString()); try (BufferedReader reader =