事象の概要
Hutool ライブラリが提供する ScriptUtils を利用して JavaScript コードを実行する際、スクリプト内に require 文が含まれていると実行失敗が発生するケースがあります。これは特に Node.js 環境で開発されたモジュールを Java 上で動かそうとした際に顕著に現れる問題です。
エラー発生メカニズム
Hutool の ScriptUtils は、デフォルト設定において JDK 標準搭載の Nashorn エンジンを利用しています。Nashorn は Java 8 で導入されたもので、ECMAScript 5.1 仕様に基づいて実装されています。一方、require によるモジュール読み込みは Node.js 固有の CommonJS 規格であり、ECMAScript 標準仕様には含まれていません。この規格の不一致がエラーの根本原因となります。
具体的なエラー内容
例えば、以下のようなスクリプトを実行しようとすると異常が発生します。
const _ = require('lodash');
function process(data) {
return data;
}
この場合、ScriptRuntimeException がスローされ、以下のようなメッセージが出力されます。
Expected an operand but found const
これは Nashorn エンジンが ES6 の構文である const や、Node.js 固有の関数である require を解釈できないことを示しています。
対処法
1. ES5 互換構文への書き換え
スクリプト側を Nashorn が理解できる形式に変更する方法です。変数宣言を var に変更し、モジュール読み込み部分を環境に合わせて調整します。
var _ = Packages.require('lodash');
function process(data) {
return data;
}
ただし、この方法でも環境によっては require が認識されない可能性があるため、単純なロジックに限定されます。
2. JavaScript エンジンの変更
より現代的なエンジンへの切り替えが根本的な解決策となります。
- GraalVM JavaScript エンジン: 最新の ECMAScript 仕様に対応しており、Node.js との互換性も高いです。
- Rhino エンジン: Mozilla 由来の Java 実装エンジンで、設定により柔軟な動作が可能です。
- J2V8 などのブリッジ: 実際の Node.js 環境を Java から呼び出す手法です。
3. 事前処理による回避
Java 側でスクリプト文字列を事前に解析し、require 文を削除したり、実際に必要なコードに置き換える処理を挟む方法もあります。
現代の Java 環境における選定基準
Nashorn エンジンは Java 15 以降で廃止予定となっているため、長期的な視点での選定が重要です。
- 簡易なスクリプト実行であれば、ES5 準拠の構文に制限して Nashorn を利用し続けることも可能です。
- 複雑なモジュール依存関係がある場合は、GraalVM の導入を検討すべきです。
- マイクロサービス架构などが採用されている場合、JavaScript 側のロジックを Node.js サービスとして独立させ、HTTP 経由で連携する_architecture_も有効な選択肢です。
これらの技術的特性を理解し、プロジェクトの要件に合わせて適切な実行環境を選定することで、互換性問題を未然に防ぐことができます。