Can NodeRuntime.await() not blocks with starting a server ?
I try to manage the life cycle of the NodeJS Runtime based on the previous example you provide in #22 (with minor simplification and without using 'eval' :P) to make a MWE.
Here is the problem:
The js script that run the server
const http = require("http");
const requestListener = function (req, res) {
res.writeHead(200, { 'Content-Type':'text/html' });
res.end("It works!");
};
const server = http.createServer(requestListener);
server.listen(8080, "localhost", () => {
console.log(`Server is running on http://localhost:8080`);
});
console.log(`Enf of javascript`);
The java main application
public class TestExpress2 {
private static final Path main_script = Paths.get(JavetOSUtils.WORKING_DIRECTORY)
.resolve("src/main/javascript/server.demo/")
.resolve("main_test.js");
private static final boolean letStartNodeJS = true;
private static final boolean skipBlocking = false;
public static void main(String[] args) throws InterruptedException {
System.out.println("Program started.");
Internal test = new Internal();
test.startServer();
if (letStartNodeJS)
Thread.sleep(2000);
test.stopServer();
System.out.println("Program terminated.");
}
static class Internal {
AtomicBoolean isWorking = new AtomicBoolean(false);
Thread nodejsThread = null;
public void startServer() {
isWorking.set(true);
nodejsThread = new Thread(this::internalNodeJSRuntime);
nodejsThread.start();
}
public void stopServer() {
isWorking.set(false);
try {
if(!skipBlocking) {
nodejsThread.join();
nodejsThread = null;
}
} catch (InterruptedException e) { }
}
public void internalNodeJSRuntime() {
System.out.println("Starting the nodejs thread.");
try (NodeRuntime nodeRuntime = V8Host.getNodeInstance().createV8Runtime()) {
nodeRuntime.getExecutor(main_script).executeVoid();
while (isWorking.get()) {
try {
System.out.print("non blocking await...");
nodeRuntime.await();
System.out.println(" done!");
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println("worker thread interrupted!");
return;
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (JavetException e) {
e.printStackTrace();
} finally {
System.out.println("All Javet objects has been deallocated...");
}
System.out.println("Stopping the worker thread.");
}
}
}
nodeRuntime.await(); is blocking the java thread which is not the defined behavior.
- What did I miss ?
- And is it reasonable to consider I can do this kind of things ?
- Or starting a server is the final action available (see #22 for multiple actions) ?
Best Regards
Blocking or non-blocking depends on how the JS code is written.
- If the JS code makes a one-time async call,
await()will be released after that call is completed. - If the JS code makes a recurrent async call,
await()will block forever.
You may explicitly call close() on a special request to see if await() is released.
Hi caoccao,
I try your suggestion adding a 'close()' call somewhere in another thread in method 'nodeJSKiller(...)' but I have a JVM crash, here is the code:
public class TestExpress2 {
private static final Path main_script = Paths.get(JavetOSUtils.WORKING_DIRECTORY)
.resolve("src/main/javascript/server.demo/")
.resolve("main_test.js");
private static final boolean letStartNodeJS = true;
private static final boolean skipBlocking = false;
public static void main(String[] args) throws InterruptedException {
System.out.println("Program started.");
Internal test = new Internal();
test.startServer();
if (letStartNodeJS)
Thread.sleep(2000);
test.stopServer();
System.out.println("Program terminated.");
}
static class Internal {
AtomicBoolean isWorking = new AtomicBoolean(false);
Thread nodejsThread = null;
public void startServer() {
isWorking.set(true);
nodejsThread = new Thread(this::internalNodeJSRuntime);
nodejsThread.start();
}
public void stopServer() {
isWorking.set(false);
try {
if(!skipBlocking) {
nodejsThread.join();
nodejsThread = null;
}
} catch (InterruptedException e) { }
}
public void internalNodeJSRuntime() {
System.out.println("Starting the nodejs thread.");
try (NodeRuntime nodeRuntime = V8Host.getNodeInstance().createV8Runtime()) {
nodeRuntime.getExecutor(main_script).execute();
nodeJSKiller(nodeRuntime);
while (isWorking.get()) {
try {
System.out.print("non blocking await...");
nodeRuntime.await();
System.out.println(" done!");
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println("worker thread interrupted!");
return;
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (JavetException e) {
e.printStackTrace();
} finally {
System.out.println("All Javet objects has been deallocated...");
}
System.out.println("Stopping the worker thread.");
}
private void nodeJSKiller(NodeRuntime _runtime) {
new Thread(() -> {
try {
Thread.sleep(3000);
System.err.print("interrupt Node Runtime...");
_runtime.close();
// System.err.println(" done! " + _runtime.isInUse() + "/" + _runtime.isClosed() + "/" + _runtime.isDead());
} catch (InterruptedException e) { e.printStackTrace();
} catch (JavetException e) { e.printStackTrace(); }
}).start();
}
}
}
Could you give it a try ? I used the corretto JVM on linux...
Regards
PS: I have update to 2.0.2 ;)
That's not what I meant. The close() is a JS call to the JS web server. You could get the instance of the JS web server, invoke the close.
Okay, my mistake !
To be short, It works, here is the full code:
main_test2.js
const http = require("http");
const requestListener = function (req, res) {
res.writeHead(200, { 'Content-Type':'text/html' });
res.end("It works!");
};
const server = http.createServer(requestListener);
function startServer() {
server.listen(8080, "localhost", () => {
console.log(`Server is running on http://localhost:8080`);
});
}
function stopServer() {
server.close();
}
TestExpress3.java
public class TestExpress3 {
private static final String local_relative_path = JavetOSUtils.WORKING_DIRECTORY + "/src/main/javascript/server.demo/";
private static final Path main_script = Paths.get(local_relative_path, "main_test2.js");
public static void main(String[] args) {
Internal test = new Internal();
test.startServer();
try { Thread.sleep(2000);
} catch (InterruptedException e) { }
// JavaFX.launch(JavaWebClient.class);
try { Thread.sleep(5000);
} catch (InterruptedException e) { }
test.stopServer();
}
static class Internal {
NodeRuntime nodeRuntime = null;
AtomicBoolean isWorking = new AtomicBoolean(false);
Thread workerThread = null;
public void startServer() {
System.out.println("Starting the main thread.");
isWorking.set(true);
workerThread = new Thread(this::internalNodeJSWrapper);
workerThread . start();
}
public void stopServer() {
try {
System.out.println("Stopping the main thread.");
isWorking.set(false);
workerThread . join();
workerThread = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void internalNodeJSWrapper() {
try (JavetEnginePool<NodeRuntime> javetEnginePool = new JavetEnginePool<NodeRuntime>()) {
javetEnginePool.getConfig().setJSRuntimeType(JSRuntimeType.Node);
try (IJavetEngine<NodeRuntime> iJavetEngine = javetEnginePool.getEngine()) {
nodeRuntime = iJavetEngine.getV8Runtime();
nodeRuntime . allowEval(true);
IV8Executor v8exec = nodeRuntime.getExecutor(main_script);
nodeRuntime.getNodeModule(NodeModuleModule.class).setRequireRootDirectory(local_relative_path);
v8exec.executeVoid();
nodeRuntime.getGlobalObject().invokeVoid("startServer");
System.out.println("Starting the worker thread.");
while (isWorking.get()) {
try {
System.out.print("non blocking await...");
nodeJSKiller(nodeRuntime);
nodeRuntime.await();
System.out.println(" done!");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Stopping the worker thread.");
}
} catch (JavetException e) {
e.printStackTrace();
}
}
private void nodeJSKiller(NodeRuntime _runtime) {
new Thread(() -> {
try {
Thread.sleep(10000);
System.err.print("interrupt Node Runtime...");
nodeRuntime.getGlobalObject().invokeVoid("stopServer");
System.err.println("done!");
} catch (InterruptedException e) { e.printStackTrace();
} catch (JavetException e) { e.printStackTrace(); }
}).start();
}
}
}
I thought that it will not be possible to interact with the runtime as await() not return but it is not the case, we still can push any request to the NodeRuntime and get the expected behavior and manage fully the life cycle of the server !
It's great!
Thank you ;)
Now I can try my test server with express and only rely on JAVA to start and stop the solution :)