From 6676cdd5b2fdf4b1b20bfacd1cc295fbca861f14 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 16 Nov 2020 22:58:59 -0800 Subject: [PATCH] feat(driver): extract driver into temp folder when running playwright (#74) --- .github/workflows/test.yml | 20 +----- CONTRIBUTING.md | 10 +-- .../microsoft/playwright/impl/Connection.java | 5 ++ .../playwright/impl/PlaywrightImpl.java | 64 +++++++++++++++---- .../microsoft/playwright/impl/Transport.java | 30 +++++++-- playwright/src/main/resources/.gitignore | 1 + scripts/download_driver.sh | 44 +++++++++++++ 7 files changed, 131 insertions(+), 43 deletions(-) create mode 100644 playwright/src/main/resources/.gitignore create mode 100755 scripts/download_driver.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a67751f8..b88d3209 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,27 +20,9 @@ jobs: uses: actions/setup-java@v1 with: java-version: 1.8 - - run: mkdir -p driver - name: Install driver - working-directory: driver - env: - OS: ${{ matrix.os }} shell: bash - run: | - FILE_NAME="unkwnown" - if [[ $OS == *"ubuntu"* ]]; then - FILE_NAME=playwright-cli-0.160.0-next.1604357190081-linux.zip - fi - if [[ $OS == *"macos"* ]]; then - FILE_NAME=playwright-cli-0.160.0-next.1604357190081-mac.zip - fi - if [[ $OS == *"windows"* ]]; then - FILE_NAME=playwright-cli-0.160.0-next.1604357190081-win32_x64.zip - fi - echo "Downloading from $FILE_NAME" - curl -O https://playwright.azureedge.net/builds/cli/next/$FILE_NAME - unzip $FILE_NAME -d . - ./playwright-cli install + run: scripts/download_driver.sh - name: Build with Maven run: mvn -B package -D skipTests --no-transfer-progress - name: Run tests diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d7e28a1c..a6338dd0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,17 +11,11 @@ git clone https://github.com/microsoft/playwright-java cd playwright-java ``` -2. Download playwright-cli binary package into `driver/` directory and run `install` command. It will install Playwright and download browser binaries for Chromium, Firefox and WebKit. +2. Run the following script to download playwright-cli binary for your platform into `playwright/src/main/resources/driver/` directory. It will also install Playwright and download browser binaries for Chromium, Firefox and WebKit. ```bash -mkdir driver -cd driver -FILENAME=playwright-cli-0.160.0-next.1604373941495-linux.zip -curl -O https://playwright.azureedge.net/builds/cli/next/${FILENAME} -unzip ${FILENAME} -d . -./playwright-cli install +scripts/download_driver.sh ``` -Replace `-linux.zip` in the file name with `-win32_x64.zip` or `-mac.zip` for other platforms. Names of published driver archives can be found at https://github.com/microsoft/playwright-cli/actions diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/Connection.java b/playwright/src/main/java/com/microsoft/playwright/impl/Connection.java index ef77a9fb..2cba7c24 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/Connection.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/Connection.java @@ -20,6 +20,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.microsoft.playwright.PlaywrightException; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.time.Duration; @@ -68,6 +69,10 @@ public class Connection { root = new Root(this); } + void close() throws IOException { + transport.close(); + } + public JsonElement sendMessage(String guid, String method, JsonObject params) { return (JsonElement) root.toDeferred(sendMessageAsync(guid, method, params)).get(); } diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/PlaywrightImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/PlaywrightImpl.java index ff32aa9f..828adac0 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/PlaywrightImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/PlaywrightImpl.java @@ -17,34 +17,68 @@ package com.microsoft.playwright.impl; import com.google.gson.Gson; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.microsoft.playwright.*; +import com.microsoft.playwright.DeviceDescriptor; +import com.microsoft.playwright.Playwright; +import com.microsoft.playwright.PlaywrightException; +import com.microsoft.playwright.Selectors; -import java.io.File; import java.io.IOException; -import java.nio.file.FileSystems; -import java.util.HashMap; -import java.util.Map; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; public class PlaywrightImpl extends ChannelOwner implements Playwright { + private static Path driverTempDir; + public static PlaywrightImpl create() { try { - File cwd = FileSystems.getDefault().getPath(".").toFile(); - File driver = new File(cwd, "../driver/playwright-cli"); - ProcessBuilder pb = new ProcessBuilder(driver.getCanonicalPath(), "run-driver"); + Path driver = ensureDriverExtracted(); + ProcessBuilder pb = new ProcessBuilder(driver.toString(), "run-driver"); pb.redirectError(ProcessBuilder.Redirect.INHERIT); // pb.environment().put("DEBUG", "pw:pro*"); Process p = pb.start(); Connection connection = new Connection(p.getInputStream(), p.getOutputStream()); - PlaywrightImpl playwright = (PlaywrightImpl)connection.waitForObjectWithKnownName("Playwright"); - return playwright; + return (PlaywrightImpl) connection.waitForObjectWithKnownName("Playwright"); } catch (IOException e) { throw new PlaywrightException("Failed to launch driver", e); } } + private static Path ensureDriverExtracted() { + if (driverTempDir == null) { + try { + driverTempDir = Files.createTempDirectory("playwright-java-"); + driverTempDir.toFile().deleteOnExit(); + ClassLoader classloader = Thread.currentThread().getContextClassLoader(); + Path path = Paths.get(classloader.getResource("driver").toURI()); + Files.list(path).forEach(filePath -> { + try { + extractResource(filePath, driverTempDir); + } catch (IOException e) { + throw new RuntimeException("Failed to extract driver from " + path, e); + } + }); + } catch (IOException | URISyntaxException e) { + throw new PlaywrightException("Failed to launch driver", e); + } + } + // TODO: remove dir on exit + return driverTempDir.resolve("playwright-cli"); + } + + private static Path extractResource(Path from, Path toDir) throws IOException { + Path path = toDir.resolve(from.getFileName()); + Files.copy(from, path); + path.toFile().setExecutable(true); + path.toFile().deleteOnExit(); + System.out.println("extracting: " + from.toString() + " to " + path.toString()); + return path; + } + private final BrowserTypeImpl chromium; private final BrowserTypeImpl firefox; private final BrowserTypeImpl webkit; @@ -91,4 +125,12 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright { public Selectors selectors() { return selectors; } + + public void close() { + try { + connection.close(); + } catch (IOException e) { + throw new PlaywrightException("Failed to close", e); + } + } } diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/Transport.java b/playwright/src/main/java/com/microsoft/playwright/impl/Transport.java index dc7cd723..c117bb60 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/Transport.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/Transport.java @@ -31,17 +31,21 @@ public class Transport { private final ReaderThread readerThread; private final WriterThread writerThread; + private boolean isClosed; + Transport(InputStream input, OutputStream output) { DataInputStream in = new DataInputStream(new BufferedInputStream(input)); readerThread = new ReaderThread(in, incoming); readerThread.start(); - // TODO: buffer? DataOutputStream out = new DataOutputStream(output); writerThread = new WriterThread(out, outgoing); writerThread.start(); } public void send(String message) { + if (isClosed) { + throw new PlaywrightException("Playwright connection closed"); + } try { outgoing.put(message); } catch (InterruptedException e) { @@ -50,16 +54,30 @@ public class Transport { } public String poll(Duration timeout) { + if (isClosed) { + throw new PlaywrightException("Playwright connection closed"); + } try { return incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new PlaywrightException("Failed to read message", e); } } + + void close() throws IOException { + if (isClosed) { + return; + } + isClosed = true; + readerThread.interrupt(); + readerThread.in.close(); + writerThread.interrupt(); + writerThread.out.close(); + } } class ReaderThread extends Thread { - private final DataInputStream in; + final DataInputStream in; private final BlockingQueue queue; private static int readIntLE(DataInputStream in) throws IOException { @@ -85,7 +103,8 @@ class ReaderThread extends Thread { try { queue.put(readMessage()); } catch (IOException e) { - e.printStackTrace(); + if (!isInterrupted()) + e.printStackTrace(); break; } catch (InterruptedException e) { break; @@ -102,7 +121,7 @@ class ReaderThread extends Thread { } class WriterThread extends Thread { - private final DataOutputStream out; + final DataOutputStream out; private final BlockingQueue queue; private static void writeIntLE(DataOutputStream out, int v) throws IOException { @@ -125,7 +144,8 @@ class WriterThread extends Thread { out.flush(); sendMessage(queue.take()); } catch (IOException e) { - e.printStackTrace(); + if (!isInterrupted()) + e.printStackTrace(); break; } catch (InterruptedException e) { break; diff --git a/playwright/src/main/resources/.gitignore b/playwright/src/main/resources/.gitignore new file mode 100644 index 00000000..fc7066f2 --- /dev/null +++ b/playwright/src/main/resources/.gitignore @@ -0,0 +1 @@ +driver/ diff --git a/scripts/download_driver.sh b/scripts/download_driver.sh new file mode 100755 index 00000000..c37d678c --- /dev/null +++ b/scripts/download_driver.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -e +set +x + +FILE_PREFIX=playwright-cli-0.160.0-next.1604373941495 + +trap "cd $(pwd -P)" EXIT +cd "$(dirname $0)" + +cd ../playwright/src/main/resources +if [[ -d driver ]]; then + echo "$(pwd)/driver already exists, delete it first" + exit 1; +fi + +mkdir driver +cd driver +echo "Created directory: $(pwd)" + +FILE_NAME="unknown" +case $(uname) in +Darwin) + FILE_NAME=${FILE_PREFIX}-mac.zip + echo "Downloading driver for macOS" + ;; +Linux) + FILE_NAME=${FILE_PREFIX}-linux.zip + echo "Downloading driver for Linux" + ;; +MINGW*) + FILE_NAME=${FILE_PREFIX}-win32_x64.zip + echo "Downloading driver for Windows" + ;; +*) + echo "Unknown platform '$(uname)'" + exit 1; + ;; +esac + +curl -O https://playwright.azureedge.net/builds/cli/next/${FILE_NAME} +unzip ${FILE_NAME} -d . +rm $FILE_NAME +./playwright-cli install