java勉強：J2V8とJavetの比較

V8はGoogleが開発するオープンソースのJavaScriptエンジンです。高速、最新ES適用など、いろいろほめられています。V8はC++のものですが、javaからV8から利用する場合、ラッパーが必要です。

- 一番有名なのは、J2V8です。だが更新が止まっているみたいで、windows向けのバージョンは2016年の4.6.0まで、async/awaitはサポートしていないです。詳細は以下のリンクから確認してください。

https://github.com/eclipsesource/J2V8

https://repo1.maven.org/maven2/com/eclipsesource/j2v8/j2v8_win32_x86_64/

- ２番めは、javetです。V8の2023年10月のv11.8.172.15、node.jsも9月v20.8.0を利用されています。認知度がJ2V8よりちょっと低いかもしれません。後発ですからJ2V8より使い勝手がいいです。

https://github.com/caoccao/Javet

```xml
<dependency>
    <groupId>com.caoccao.javet</groupId>
    <artifactId>javet</artifactId>
    <version>3.1.8</version>
</dependency>
```

# J2V8とJavetの使い勝手の違い
testa.js
```js
global.myVar="aaaaaaaaaa";
```
testb.js
```js
global.myVar="bbbbbbbbbb";
```
testJ2V8.java
```java
import java.io.File;
import java.io.IOException;

import com.caoccao.javet.exceptions.JavetException;
import com.eclipsesource.v8.NodeJS;

public class TestJ2V8 {
	public static void main(String[] args) throws IOException,  InterruptedException, JavetException {
		final NodeJS nodeJS = NodeJS.createNodeJS();
		File testa=new File("testa.js");
		nodeJS.exec(testa);
		File testb=new File("testb.js");
		nodeJS.exec(testb);
		while (nodeJS.isRunning()) {//実行完了まで待つ
			nodeJS.handleMessage();
		}
		System.out.println(nodeJS.getRuntime().getString("myVar"));
		nodeJS.release();
	}
}
```
TestJavet.java
```java
import java.io.File;
import java.io.IOException;

import com.caoccao.javet.exceptions.JavetException;
import com.caoccao.javet.interop.V8Host;
import com.caoccao.javet.interop.V8Runtime;
import com.caoccao.javet.interop.executors.IV8Executor;

public class TestJavet {
	public static void main(String[] args) throws IOException,  InterruptedException, JavetException {
		try (V8Runtime v8Runtime = V8Host.getNodeInstance().createV8Runtime()) {
			IV8Executor iV8Executor=v8Runtime.getExecutor(new File("testa.js"));
			iV8Executor.executeVoid();
			v8Runtime.await();//実行完了まで待つ
			iV8Executor=v8Runtime.getExecutor(new File("testb.js"));
			iV8Executor.executeVoid();
			v8Runtime.await();//実行完了まで待つ
			System.out.println(v8Runtime.getGlobalObject().getString("myVar"));
		}
	}
}
```
javetのv8Runtime.await()呼び出しは、j2v8のwhile (nodeJS.isRunning()) と類似役割の目的ですが、v8Runtime.await()は何回呼び出しても構わないです。while (nodeJS.isRunning())は1回しか呼び出せなくて、実行後V8は停止状態になってこれ以上jsファイルを実行できません。j2v8には、v8Runtime.await()に相当するものがありません。だって2016年のものですから。

そして、j2v8は、efwのloadWithGlobalPool関数の内蔵javaScriptエンジンとして利用することは不可能です。プールから取得して利用して返すことは、エンジンの利用は１回限りではなく、断続で何回にもなるからです。

また、javetは、Primitive ConverterとObject Converterの仕組みがあって、javaとjavascript間の変換が自動的に行ってくれます。j2v8にはPrimitive Converterの類似仕組みがありますが、Object Converterと同等するものがありません。そして、Byte[]をjavaからエンジンに渡す場合、手動でV8Arrayに変換してエンジンに渡しています。その分の勉強と手間が発生しなければなりません。

https://www.caoccao.com/Javet/reference/converters/primitive_converter.html

https://www.caoccao.com/Javet/reference/converters/object_converter.html

# J2V8とJavetのマルチスレッドの違い
結論からいうと、J2V8はマルチスレッド禁止です。JavetはマルチスレッドがOKです。
- まずJ2V8とJavetのラインタイムをstatic変数に定義します。
```java
package test;
import com.eclipsesource.v8.V8;
public class TestJ2V82 {
	public static V8 runtime = V8.createV8Runtime();
	static {
		runtime = V8.createV8Runtime();
		runtime.executeVoidScript("var c=0;function hello(){c++;return c;};");
	}
}
```
```java
package test;
import com.caoccao.javet.exceptions.JavetException;
import com.caoccao.javet.interop.V8Host;
import com.caoccao.javet.interop.V8Runtime;
public class TestJavet2 {
	public static V8Runtime v8Runtime;
	static {
		try {
			v8Runtime = V8Host.getNodeInstance().createV8Runtime();
			v8Runtime.getExecutor("var c=0;function hello(){c++;return c;};").executeVoid();
		} catch (JavetException e) {
			e.printStackTrace();
		}
	}
}
```
それぞれのstatic変数を利用するjspを作成します。
test_subJ2V8.jsp
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="test.TestJ2V82"%>
<%@ page import="com.eclipsesource.v8.V8"%>

<%
	V8 runtime =TestJ2V82.runtime;
	out.println(runtime.executeStringScript("''+hello();"));
	out.println("<br>");
	out.println(runtime.executeStringScript("''+hello();"));
%>
```
test_subJavet.jsp
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="test.TestJavet2"%>
<%@ page import="java.io.File"%>
<%@ page import="java.io.IOException"%>
<%@ page import="com.caoccao.javet.exceptions.JavetException"%>
<%@ page import="com.caoccao.javet.interop.V8Host"%>
<%@ page import="com.caoccao.javet.interop.V8Runtime"%>
<%@ page import="com.caoccao.javet.interop.executors.IV8Executor"%>

<%
	V8Runtime v8Runtime=TestJavet2.v8Runtime;
	out.println(v8Runtime.getExecutor("hello()").executeInteger());
	out.println("<br>");
	out.println(v8Runtime.getExecutor("hello()").executeInteger());
%>
```
上記jspを同時10回を呼び出すテスト画面を作ります。
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="efw" uri="efw" %> 
<!DOCTYPE HTML>
<HTML>
<HEAD>
	<META HTTP-EQUIV="CONTENT-TYPE"CONTENT="TEXT/HTML;CHARSET=UTF-8">       
	<TITLE>efw Output Test</TITLE>
	<!-- Efwクライアントの取り込み-->
	<efw:Client lang="jp"/>
	<script>
	function testJavetM(flg){
		$("#pdf0")[0].src="";
		$("#pdf1")[0].src="";
		$("#pdf2")[0].src="";
		$("#pdf3")[0].src="";
		$("#pdf4")[0].src="";
		$("#pdf5")[0].src="";
		$("#pdf6")[0].src="";
		$("#pdf7")[0].src="";
		$("#pdf8")[0].src="";
		$("#pdf9")[0].src="";
		setTimeout(function(){
			$("#pdf0")[0].src="test_sub"+flg+".jsp";
			$("#pdf1")[0].src="test_sub"+flg+".jsp";
			$("#pdf2")[0].src="test_sub"+flg+".jsp";
			$("#pdf3")[0].src="test_sub"+flg+".jsp";
			$("#pdf4")[0].src="test_sub"+flg+".jsp";
			$("#pdf5")[0].src="test_sub"+flg+".jsp";
			$("#pdf6")[0].src="test_sub"+flg+".jsp";
			$("#pdf7")[0].src="test_sub"+flg+".jsp";
			$("#pdf8")[0].src="test_sub"+flg+".jsp";
			$("#pdf9")[0].src="test_sub"+flg+".jsp";
		},100);
	}
	</script>
</HEAD>
<BODY>
<button onclick="testJavetM('Javet');">マルチJavet</button>
<button onclick="testJavetM('J2V8');">マルチJ2V8</button>
<br>
<iframe id="pdf0" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf1" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf2" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf3" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf4" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf5" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf6" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf7" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf8" src="" style="width:400px;height:300px"></iframe>
<iframe id="pdf9" src="" style="width:400px;height:300px"></iframe>
</BODY>
</HTML>
```
実行の結果はこれです。
J2V8は、1回目成功、その後は全部失敗です。失敗の理由は、org.apache.jasper.JasperException: javax.servlet.ServletException: java.lang.Error: Invalid V8 thread access　です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/7208c870-d781-010b-b15d-d092738c6109.png)

Javetは全部成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/e5da66ac-51bb-6296-c6b9-97b5dd44c44e.png)

- マルチスレッドについて、Graaljsもだめです。QuickJSもだめです。逆にむかしのNashornとRhinoは可能です。ようやくES2015以上サポート且つマルチ可能のエンジンを見つかりました。<font color=red>※あとで分かったが、Javetのマルチスレッドは表だけで、真のマルチではありません。</font>

# 結論
Javetはいいね。これから利用してみましょう。