mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
feat(driver): launch driver per playwright instance (#75)
This commit is contained in:
parent
6676cdd5b2
commit
c05b6409f3
@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) Microsoft Corporation.
|
* Copyright (c) Microsoft Corporation.
|
||||||
* <p>
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
* <p>
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
* <p>
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -18,10 +18,9 @@ package com.microsoft.playwright;
|
|||||||
|
|
||||||
import com.microsoft.playwright.impl.PlaywrightImpl;
|
import com.microsoft.playwright.impl.PlaywrightImpl;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface Playwright {
|
public interface Playwright extends AutoCloseable {
|
||||||
static Playwright create() {
|
static Playwright create() {
|
||||||
return PlaywrightImpl.create();
|
return PlaywrightImpl.create();
|
||||||
}
|
}
|
||||||
@ -33,4 +32,7 @@ public interface Playwright {
|
|||||||
Map<String, DeviceDescriptor> devices();
|
Map<String, DeviceDescriptor> devices();
|
||||||
|
|
||||||
Selectors selectors();
|
Selectors selectors();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void close() throws Exception;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import java.io.File;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) throws Exception {
|
||||||
Playwright playwright = Playwright.create();
|
Playwright playwright = Playwright.create();
|
||||||
Browser browser = playwright.chromium().launch();
|
Browser browser = playwright.chromium().launch();
|
||||||
BrowserContext context = browser.newContext(
|
BrowserContext context = browser.newContext(
|
||||||
@ -31,5 +31,6 @@ public class Main {
|
|||||||
page.click("text=check feature status");
|
page.click("text=check feature status");
|
||||||
page.screenshot(new Page.ScreenshotOptions().withPath(Paths.get("s.png")));
|
page.screenshot(new Page.ScreenshotOptions().withPath(Paths.get("s.png")));
|
||||||
browser.close();
|
browser.close();
|
||||||
|
playwright.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,28 +29,32 @@ import java.net.URISyntaxException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||||
private static Path driverTempDir;
|
private static Path driverTempDir;
|
||||||
|
private Process driverProcess;
|
||||||
|
|
||||||
public static PlaywrightImpl create() {
|
public static PlaywrightImpl create() {
|
||||||
try {
|
try {
|
||||||
Path driver = ensureDriverExtracted();
|
Path driver = ensureDriverInstalled();
|
||||||
ProcessBuilder pb = new ProcessBuilder(driver.toString(), "run-driver");
|
ProcessBuilder pb = new ProcessBuilder(driver.toString(), "run-driver");
|
||||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||||
// pb.environment().put("DEBUG", "pw:pro*");
|
// pb.environment().put("DEBUG", "pw:pro*");
|
||||||
Process p = pb.start();
|
Process p = pb.start();
|
||||||
Connection connection = new Connection(p.getInputStream(), p.getOutputStream());
|
Connection connection = new Connection(p.getInputStream(), p.getOutputStream());
|
||||||
return (PlaywrightImpl) connection.waitForObjectWithKnownName("Playwright");
|
PlaywrightImpl result = (PlaywrightImpl) connection.waitForObjectWithKnownName("Playwright");
|
||||||
} catch (IOException e) {
|
result.driverProcess = p;
|
||||||
|
return result;
|
||||||
|
} catch (IOException | InterruptedException | URISyntaxException e) {
|
||||||
throw new PlaywrightException("Failed to launch driver", e);
|
throw new PlaywrightException("Failed to launch driver", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Path ensureDriverExtracted() {
|
private static synchronized Path ensureDriverInstalled() throws IOException, InterruptedException, URISyntaxException {
|
||||||
if (driverTempDir == null) {
|
if (driverTempDir == null) {
|
||||||
try {
|
|
||||||
driverTempDir = Files.createTempDirectory("playwright-java-");
|
driverTempDir = Files.createTempDirectory("playwright-java-");
|
||||||
driverTempDir.toFile().deleteOnExit();
|
driverTempDir.toFile().deleteOnExit();
|
||||||
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
|
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
|
||||||
@ -59,14 +63,20 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
|||||||
try {
|
try {
|
||||||
extractResource(filePath, driverTempDir);
|
extractResource(filePath, driverTempDir);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Failed to extract driver from " + path, e);
|
throw new PlaywrightException("Failed to extract driver from " + path, e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (IOException | URISyntaxException e) {
|
|
||||||
throw new PlaywrightException("Failed to launch driver", e);
|
Path driver = driverTempDir.resolve("playwright-cli");
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(driver.toString(), "install");
|
||||||
|
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||||
|
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||||
|
Process p = pb.start();
|
||||||
|
boolean result = p.waitFor(10, TimeUnit.MINUTES);
|
||||||
|
if (!result) {
|
||||||
|
System.err.println("Timed out waiting for browsers to install");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: remove dir on exit
|
|
||||||
return driverTempDir.resolve("playwright-cli");
|
return driverTempDir.resolve("playwright-cli");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +85,7 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
|||||||
Files.copy(from, path);
|
Files.copy(from, path);
|
||||||
path.toFile().setExecutable(true);
|
path.toFile().setExecutable(true);
|
||||||
path.toFile().deleteOnExit();
|
path.toFile().deleteOnExit();
|
||||||
System.out.println("extracting: " + from.toString() + " to " + path.toString());
|
// System.out.println("extracting: " + from.toString() + " to " + path.toString());
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +95,7 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
|||||||
private final Selectors selectors;
|
private final Selectors selectors;
|
||||||
private final Map<String, DeviceDescriptor> devices = new HashMap<>();
|
private final Map<String, DeviceDescriptor> devices = new HashMap<>();
|
||||||
|
|
||||||
public PlaywrightImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
PlaywrightImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
chromium = parent.connection.getExistingObject(initializer.getAsJsonObject("chromium").get("guid").getAsString());
|
chromium = parent.connection.getExistingObject(initializer.getAsJsonObject("chromium").get("guid").getAsString());
|
||||||
firefox = parent.connection.getExistingObject(initializer.getAsJsonObject("firefox").get("guid").getAsString());
|
firefox = parent.connection.getExistingObject(initializer.getAsJsonObject("firefox").get("guid").getAsString());
|
||||||
@ -126,11 +136,13 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
|||||||
return selectors;
|
return selectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
@Override
|
||||||
try {
|
public void close() throws Exception {
|
||||||
connection.close();
|
connection.close();
|
||||||
} catch (IOException e) {
|
// playwright-cli will exit when its stdin is closed, we wait for that.
|
||||||
throw new PlaywrightException("Failed to close", e);
|
boolean didClose = driverProcess.waitFor(30, TimeUnit.SECONDS);
|
||||||
|
if (!didClose) {
|
||||||
|
System.err.println("WARNING: Timed out while waiting for driver process to exit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,16 +69,19 @@ public class Transport {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isClosed = true;
|
isClosed = true;
|
||||||
readerThread.interrupt();
|
// We interrupt only the outgoing pipe and keep reader thread running as
|
||||||
readerThread.in.close();
|
// otherwise child process may block on writing to its stdout and never
|
||||||
writerThread.interrupt();
|
// exit (observed on Windows).
|
||||||
|
readerThread.isClosing = true;
|
||||||
writerThread.out.close();
|
writerThread.out.close();
|
||||||
|
writerThread.interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReaderThread extends Thread {
|
class ReaderThread extends Thread {
|
||||||
final DataInputStream in;
|
private final DataInputStream in;
|
||||||
private final BlockingQueue<String> queue;
|
private final BlockingQueue<String> queue;
|
||||||
|
volatile boolean isClosing;
|
||||||
|
|
||||||
private static int readIntLE(DataInputStream in) throws IOException {
|
private static int readIntLE(DataInputStream in) throws IOException {
|
||||||
int ch1 = in.read();
|
int ch1 = in.read();
|
||||||
@ -103,8 +106,9 @@ class ReaderThread extends Thread {
|
|||||||
try {
|
try {
|
||||||
queue.put(readMessage());
|
queue.put(readMessage());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (!isInterrupted())
|
if (!isInterrupted() && !isClosing) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
break;
|
break;
|
||||||
|
@ -69,6 +69,12 @@ public class TestBase {
|
|||||||
httpsServer = null;
|
httpsServer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void closePlaywright() throws Exception {
|
||||||
|
playwright.close();
|
||||||
|
playwright = null;
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void createContextAndPage() {
|
void createContextAndPage() {
|
||||||
server.reset();
|
server.reset();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user