JavaScript in Java

Der kürzlich veröffentlichte JavaLobby-Beitrag Die Top 10 der nicht verwendeten Funktionen in Java war äußerst beliebt. Zum Zeitpunkt dieses Schreibens ist es der bestplatzierte Beitrag in der Kategorie DZone Top Links. Außerdem wurde eine Antwort darauf veröffentlicht. In beiden Blog-Posts gibt es viele interessante Beobachtungen zu nicht ausgelasteten Funktionen in Java, und ich stimme einigen mehr als anderen zu. Ein Punkt, der meine Aufmerksamkeit auf sich zog, war jedoch die Behauptung, dass Java SE 6 eine der am wenigsten verwendeten Java-Funktionen ist.

Ich arbeite sehr gerne mit Java SE 6 und habe in der Vergangenheit mehrmals über Java SE 6-Funktionen geschrieben oder darüber gebloggt. In diesem Blogbeitrag möchte ich einen Teil der Fähigkeit von Java SE 6 demonstrieren, JavaScript-Code als Host auszuführen.

Die meisten Java-Entwickler und JavaScript-Entwickler wissen, dass JavaScript und Java neben den vier Buchstaben "JAVA" nur sehr wenig gemeinsam haben, außer einem C-ähnlichen Erbe. Dennoch kann es manchmal nützlich sein, eine Skriptsprache aus Java-Code heraus auszuführen, und Java SE 6 ermöglicht dies.

Das Paket javax.script wurde mit Java SE 6 eingeführt und enthält Klassen, Schnittstellen und eine überprüfte Ausnahme im Zusammenhang mit der Verwendung von Skript-Engines in Java. Dieser Blogeintrag konzentriert sich auf ScriptEngineFactory, ScriptEngineManager, ScriptEngine und ScriptException.

Eines der ersten Dinge, die Sie möglicherweise tun möchten, ist festzustellen, welche Scripting-Engines bereits verfügbar sind. Der nächste Codeausschnitt zeigt, wie einfach dies mit Java SE 6 geht.

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 + " "); } } 

Der oben gezeigte Code generiert eine Ausgabe wie im nächsten Screenshot.

Wie dieses Bild zeigt, ist die Javailla-Engine von Mozilla Rhino in Suns Java SE 6 enthalten. Wir sehen auch einige "gebräuchliche Namen", die dieser bestimmten Engine zugeordnet sind. Jeder dieser Namen kann zum Nachschlagen dieser Engine verwendet werden. In späteren Beispielen in diesem Beitrag werde ich den allgemeinen Namen "js" für diese Suche verwenden.

Das nächste Codebeispiel nutzt die bereitgestellte Rhino-JavaScript-Engine, um JavaScript-Code aus Java-Code auszuführen. In diesem Fall nutzen wir die toExponential-Funktion von JavaScript.

 /** * 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()); } } 

Der obige Code ruft JavaScript direkt mit der ScriptEngine.eval (String) -Methode auf, um den bereitgestellten String mit der JavaScript-Syntax auszuwerten. Vor dem Aufruf der evalMethode werden zwei Parameter über ScriptEngine.put (String, Object) -Aufrufe an den JavaScript-Code "übergeben" (gebunden). Auf das Ergebnisobjekt des ausgeführten JavaScript wird im Java-Code mit einem ScriptEngine.get (String) -Aufruf zugegriffen.

Um den obigen Code mithilfe der toExponentialFunktion zu demonstrieren , verwende ich den folgenden "Client" -Code.

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); 

Wenn der obige Code für die zuvor gezeigte Methode writeNumberAsExponential ausgeführt wird und JavaScript verwendet wird, wird die Ausgabe ähnlich wie im nächsten Screenshot angezeigt.

Dieses Beispiel reicht aus, um zu demonstrieren, wie einfach es ist, JavaScript-Funktionen in Java SE 6 aufzurufen. Dies könnte jedoch noch allgemeiner implementiert werden, wie die nächsten beiden Beispiele zeigen werden. Das erste Beispiel zeigt den Aufruf von relativ beliebigem JavaScript ohne übergebene / gebundene Parameter, und das zweite Beispiel zeigt den Aufruf von relativ beliebigem JavaScript mit übergebenen / gebundenen Parametern.

Eine relativ beliebige JavaScript-Zeichenfolge kann mit Code verarbeitet werden, der dem nachfolgend gezeigten ähnlich ist.

 /** * 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; } 

Der obige Code bietet einiges an Flexibilität in Bezug auf das JavaScript, das verarbeitet werden kann. Dies ist wahrscheinlich nicht die beste Idee für Produktionscode, erleichtert jedoch die Demonstration der Verwendung verschiedener JavaScript-Funktionen in Java.

Das erste Beispiel für diese relativ willkürliche JavaScript-Verarbeitung nutzt das Date-Objekt von JavaScript. Der Beispielcode wird als nächstes angezeigt.

 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) ); 

Dieser Code gibt an, dass ein JavaScript-Datum abgerufen werden soll (dies ist das aktuelle Datum) und dass der Monat, das Datum des Monats und das gesamte Jahr aus diesem instanziierten Datum extrahiert werden sollen. Die Ausgabe hierfür erscheint als nächstes.

Das letzte Beispiel arbeitete mit einer beliebigen JavaScript-Zeichenfolge, verwendete jedoch keine Parameter. Das nächste Beispiel zeigt die Bereitstellung von Parametern für diese beliebige JavaScript-Zeichenfolgenverarbeitung, da die Verwendung der pow-Funktion von JavaScript demonstriert wird. Der Code für dieses Beispiel ist als nächstes aufgeführt.

 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) ); 

Die Ausgabe dieses Beispiels wird im folgenden Screenshot gezeigt.

Für mein letztes Beispiel dieses Blogpostings zeige ich die Standardausgabe toString()der ScriptExceptionin einigen der vorherigen Beispiele deklarierten. Die ScriptEngine.evalMethode löst diese aktivierte Ausnahme aus, wenn beim Ausführen / Auswerten des bereitgestellten Skripts ein Fehler auftritt. Diese Methode löst auch eine NullPointerException aus, wenn der angegebene String null ist. Der Code, der zum Erzwingen eines Skriptfehlers verwendet wird, wird als Nächstes angezeigt.

 /** * 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)); } 

Dieser Code enthält ein unsinniges Skript (in Bezug auf die JavaScript-Syntax), aber genau das ist erforderlich, um die ScriptException.toString () zu demonstrieren, die als Teil der Ausnahmebehandlung in der oben gezeigten Methode zur Behandlung einer beliebigen JavaScript-Zeichenfolge aufgerufen wird . Wenn der Code ausgeführt wird, sehen wir die Ausnahmeinformationen wie im nächsten Bild gezeigt.

Der Teil der Ausgabe, der von kommt, ScriptException.toString()ist der Teil, der besagt: "javax.script.ScriptException: sun.org.mozilla.javascript.internal.EvaluatorException: fehlt; vor Anweisung (# 1) in Zeile 1."

Das ScriptExceptionenthält den Dateinamen, die Zeilennummer und die Spaltennummer der Ausnahme. Dies ist besonders hilfreich, wenn eine Datei mit JavaScript-Code zur Auswertung bereitgestellt wird.

Fazit

Java SE 6 macht es einfach, JavaScript in Java-Code zu verwenden. Andere Scripting-Engines können ebenfalls mit Java verknüpft werden. Es ist jedoch praktisch, Mozilla Rhino standardmäßig bereitzustellen.

Vollständiger Snapshot des Code- und Ausgabebildschirms

Der Vollständigkeit halber füge ich hier die vollständige Codeliste an einer Stelle und die daraus resultierende Ausgabe danach ein.

JavaScriptInJavaExample.java