JavaのJavaScript

最近のJavaLobbyの投稿「Javaの未使用機能トップ10」は非常に人気があります。この記事の執筆時点では、DZoneトップリンクカテゴリのトップランクの投稿です。さらに、それに対する返信も投稿されています。両方のブログ投稿で、Javaの十分に活用されていない機能について多くの興味深い観察があり、私は他のものよりもいくつかに同意します。しかし、本当に私の注意を引いたのは、Java SE6が最も使用されていないJava機能の1つであるという主張でした。

私はJavaSE 6での作業を本当に楽しんでおり、過去に何度かJava SE6の機能について書いたりブログを書いたりしています。このブログ投稿では、JavaScriptコードの実行をホストするJava SE6の機能の一部を紹介する予定です。

ほとんどのJava開発者とJavaScript開発者は、「JAVA」という4文字以外に、JavaScriptとJavaには、Cのような遺産以外にほとんど共通点がないことを理解しています。それでも、Javaコード内からスクリプト言語を実行すると便利な場合があり、Java SE6ではこれが可能です。

javax.scriptパッケージはJavaSE 6で導入され、Java内でのスクリプトエンジンの使用に関連するクラス、インターフェイス、およびチェックされた例外が含まれています。このブログ投稿では、ScriptEngineFactory、ScriptEngineManager、ScriptEngine、およびScriptExceptionに焦点を当てます。

最初にやりたいことの1つは、どのスクリプトエンジンがすでに利用可能かを判断することです。次のコードスニペットは、Java SE6でこれを行うのがいかに簡単かを示しています。

final ScriptEngineManager manager = new ScriptEngineManager(); for (final ScriptEngineFactory scriptEngine : manager.getEngineFactories()) { System.out.println( scriptEngine.getEngineName() + " (" + scriptEngine.getEngineVersion() + ")" ); System.out.println( "\tLanguage: " + scriptEngine.getLanguageName() + "(" + scriptEngine.getLanguageVersion() + ")" ); System.out.println("\tCommon Names/Aliases: "); for (final String engineAlias : scriptEngine.getNames()) { System.out.println(engineAlias + " "); } } 

上記のコードは、次の画面のスナップショットに示すような出力を生成します。

この画像が示すように、Mozilla RhinoJavaScriptエンジンはSunのJavaSE 6に含まれています。また、この特定のエンジンに関連付けられている「一般的な名前」もいくつかあります。これらの名前のいずれかを使用して、このエンジンを検索できます。この投稿の後の例では、このルックアップに一般名「js」を使用します。

次のコードサンプルでは、​​提供されているRhino JavaScriptエンジンを利用して、JavaコードからJavaScriptコードを実行します。この場合、JavaScriptのtoExponential関数を利用します。

 /** * Write number in exponential form. * * @param numberToWriteInExponentialForm The number to be represented in * exponential form. * @param numberDecimalPlaces The number of decimal places to be used in the * exponential representation. */ public static void writeNumberAsExponential( final Number numberToWriteInExponentialForm, final int numberDecimalPlaces) { final ScriptEngine engine = manager.getEngineByName("js"); try { engine.put("inputNumber", numberToWriteInExponentialForm); engine.put("decimalPlaces", numberDecimalPlaces); engine.eval("var outputNumber = inputNumber.toExponential(decimalPlaces);"); final String exponentialNumber = (String) engine.get("outputNumber"); System.out.println("Number: " + exponentialNumber); } catch (ScriptException scriptException) { LOGGER.severe( "ScriptException encountered trying to write exponential: " + scriptException.toString()); } } 

上記のコードは、ScriptEngine.eval(String)メソッドを使用してJavaScriptを直接呼び出し、JavaScript構文を含む提供された文字列を評価します。evalメソッドを呼び出す前に、ScriptEngine.put(String、Object)呼び出しを介して2つのパラメーターがJavaScriptコードに「渡され」(バインドされ)ます。実行されたJavaScriptの結果オブジェクトは、ScriptEngine.get(String)呼び出しを使用してJavaコードでアクセスされます。

toExponential関数を使用して上記のコードを示すために、次の「クライアント」コードを使用します。

final int sourceNumber = 675456; writeNumberAsExponential(sourceNumber, 1, System.out); writeNumberAsExponential(sourceNumber, 2, System.out); writeNumberAsExponential(sourceNumber, 3, System.out); writeNumberAsExponential(sourceNumber, 4, System.out); writeNumberAsExponential(sourceNumber, 5, System.out); 

上記のコードを前に示したwriteNumberAsExponentialメソッドに対して実行し、JavaScriptを使用すると、次の画面のスナップショットに示すような出力が表示されます。

この例は、Java SE 6内からJavaScript機能を呼び出すことがいかに簡単であるかを示すのに十分です。ただし、次の2つの例で示すように、これはさらに一般的に実装できます。最初の例は、パラメーターが渡された/バインドされていない比較的任意のJavaScriptの呼び出しを示し、2番目の例は、パラメーターが渡された/バインドされた比較的任意のJavaScriptの呼び出しを示しています。

比較的任意のJavaScript文字列は、次に示すようなコードで処理できます。

 /** * Process the passed-in JavaScript script that should include an assignment * to a variable with the name prescribed by the provided nameOfOutput and * may include parameters prescribed by inputParameters. * * @param javaScriptCodeToProcess The String containing JavaScript code to * be evaluated. This String is not checked for any type of validity and * might possibly lead to the throwing of a ScriptException, which would * be logged. * @param nameOfOutput The name of the output variable associated with the * provided JavaScript script. * @param inputParameters Optional map of parameter names to parameter values * that might be employed in the provided JavaScript script. This map * may be null if no input parameters are expected in the script. */ public static Object processArbitraryJavaScript( final String javaScriptCodeToProcess, final String nameOfOutput, final Map inputParameters) { Object result = null; final ScriptEngine engine = manager.getEngineByName("js"); try { if (inputParameters != null) { for (final Map.Entry parameter : inputParameters.entrySet()) { engine.put(parameter.getKey(), parameter.getValue()); } } engine.eval(javaScriptCodeToProcess); result = engine.get(nameOfOutput); } catch (ScriptException scriptException) { LOGGER.severe( "ScriptException encountered trying to write arbitrary JavaScript '" + javaScriptCodeToProcess + "': " + scriptException.toString()); } return result; } 

上記のコードは、処理できるJavaScriptに関してかなりの柔軟性を提供します。これはおそらく本番コードにとって最良のアイデアではありませんが、Java内でのさまざまなJavaScript機能の使用法を簡単に示すことができます。

この比較的任意のJavaScript処理を使用する最初の例では、JavaScriptのDateオブジェクトを利用します。次にサンプルコードを示します。

 System.out.println( "Today's Date: " + processArbitraryJavaScript( "var date = new Date(); var month = (date.getMonth()+1).toFixed(0)", "month", null) + "/" + processArbitraryJavaScript( "var date = new Date(); var day = date.getDate().toFixed(0)", "day", null) + "/" + processArbitraryJavaScript( "var date = new Date(); var year = date.getFullYear().toFixed(0)", "year", null) ); 

このコードは、JavaScriptの日付(現在の日付)を取得し、その月、日付、および通年をそのインスタンス化された日付から抽出する必要があることを指定します。この出力は次に表示されます。

最後の例は、任意のJavaScript文字列で機能しましたが、パラメーターを使用しませんでした。次の例は、JavaScriptのpow関数の使用法を示すため、この任意のJavaScript文字列処理にパラメーターを提供する方法を示しています。この例のコードを次に示します。

 final Map exponentParameters = new HashMap(); exponentParameters.put("base", 2); exponentParameters.put("exponent", 5); System.out.println( "2 to the 5 is: " + processArbitraryJavaScript( "var answer = Math.pow(base,exponent)", "answer", exponentParameters) ); 

この例の実行からの出力は、次の画面スナップショットに示されています。

このブログ投稿の最後の例として、前の例のいくつかで宣言された標準toString()出力をScriptException示します。ScriptEngine.eval提供されたスクリプトを評価する/実行にエラーがある場合、メソッドは、このチェック例外をスローします。指定された文字列がnullの場合、このメソッドはNullPointerExceptionもスローします。スクリプトエラーを強制するために使用されるコードを次に示します。

 /** * Intentionally cause script handling error to show the type of information * that a ScriptException includes. */ public static void testScriptExceptionHandling() { System.out.println(processArbitraryJavaScript("Garbage In", "none", null)); } 

このコードは(JavaScript構文の観点から)無意味なスクリプトを提供しますが、これは、任意のJavaScript文字列を処理するために上記のメソッドの例外処理の一部として呼び出されるScriptException.toString()を示すために必要なものです。 。コードを実行すると、次の画像に示すような例外情報が表示されます。

からの出力の部分は、ScriptException.toString()「javax.script.ScriptException:sun.org.mozilla.javascript.internal.EvaluatorException:missing; before statement(#1)in at linenumber1」という部分です。

ScriptExceptionは、例外のファイル名、行番号、および列番号が含まれています。これは、JavaScriptコードを含むファイルが評価用に提供されている場合に特に役立ちます。

結論

Java SE 6を使用すると、Javaコード内でJavaScriptを簡単に使用できます。他のスクリプトエンジンもJavaに関連付けることができますが、MozillaRhinoにすぐに使用できるスクリプトエンジンを用意しておくと便利です。

完全なコードと出力画面のスナップショット

完全を期すために、ここでは完全なコードリストとその結果の出力をここに含めています。

JavaScriptInJavaExample.java