JavaScript en Java

Le récent article de JavaLobby Les 10 principales fonctionnalités inutilisées de Java a été extrêmement populaire. Au moment de la rédaction de cet article, il s'agit du poste le mieux classé dans la catégorie Top Links DZone. En outre, une réponse a également été publiée. Il y a de nombreuses observations intéressantes sur les fonctionnalités sous-utilisées de Java dans les deux articles de blog et je suis d'accord avec certains plus que d'autres. Cependant, l'élément qui a vraiment attiré mon attention était l'affirmation selon laquelle Java SE 6 est l'une des fonctionnalités Java les plus inutilisées.

J'aime vraiment travailler avec Java SE 6 et j'ai écrit ou blogué sur les fonctionnalités de Java SE 6 à plusieurs reprises dans le passé. Dans cet article de blog, j'ai l'intention de démontrer une partie de la capacité de Java SE 6 à héberger du code JavaScript.

La plupart des développeurs Java et JavaScript comprennent qu'outre les quatre lettres «JAVA», JavaScript et Java ont très peu de points communs à part un héritage de type C. Pourtant, il peut parfois être utile d'exécuter un langage de script à partir du code Java et Java SE 6 le permet.

Le package javax.script a été introduit avec Java SE 6 et comprend des classes, des interfaces et une exception vérifiée liée à l'utilisation de moteurs de script dans Java. Cette publication de blog se concentrera sur ScriptEngineFactory, ScriptEngineManager, ScriptEngine et ScriptException.

L'une des premières choses à faire est de déterminer quels moteurs de script sont déjà disponibles. Le prochain extrait de code montre à quel point cela est facile à faire avec Java SE 6.

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

Le code ci-dessus génère une sortie comme celle indiquée dans la capture d'écran suivante.

Comme le montre cette image, le moteur JavaScript de Mozilla Rhino est inclus avec Java SE 6. Nous voyons également quelques «noms communs» associés à ce moteur particulier. N'importe lequel de ces noms peut être utilisé pour rechercher ce moteur. Dans les exemples ultérieurs de cet article, j'utiliserai le nom commun "js" pour cette recherche.

Le prochain exemple de code tirera parti du moteur JavaScript Rhino fourni pour exécuter du code JavaScript à partir du code Java. Dans ce cas, nous profiterons de la fonction toExponential de 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()); } } 

Le code ci-dessus appelle directement JavaScript à l'aide de la méthode ScriptEngine.eval (String) pour évaluer la chaîne fournie contenant la syntaxe JavaScript. Avant l'invocation de la evalméthode, deux paramètres sont «passés» (liés) au code JavaScript via des appels ScriptEngine.put (String, Object). L'objet résultat du JavaScript exécuté est accessible dans le code Java à l'aide d'un appel ScriptEngine.get (String).

Pour illustrer le code ci-dessus en utilisant la toExponentialfonction, j'utiliserai le code "client" suivant.

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

Lorsque le code ci-dessus est exécuté sur la méthode writeNumberAsExponential présentée précédemment et que JavaScript est utilisé, la sortie apparaît similaire à celle affichée dans l'instantané d'écran suivant.

Cet exemple suffit à démontrer à quel point il est facile d'appeler la fonctionnalité JavaScript à partir de Java SE 6. Cependant, cela pourrait être implémenté de manière encore plus générique comme le montreront les deux exemples suivants. Le premier exemple montre l'invocation de JavaScript relativement arbitraire sans paramètres passés / liés et le deuxième exemple montre l'invocation de JavaScript relativement arbitraires avec des paramètres passés / liés.

Une chaîne JavaScript relativement arbitraire peut être traitée avec un code similaire à celui illustré ci-dessous.

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

Le code ci-dessus offre un peu de flexibilité en termes de JavaScript pouvant être traité. Ce n'est probablement pas la meilleure idée pour le code de production, mais cela facilite la démonstration de l'utilisation de diverses fonctionnalités JavaScript dans Java.

Le premier exemple d'utilisation de ce traitement JavaScript relativement arbitraire tire parti de l'objet Date de JavaScript. L'exemple de code est illustré ci-dessous.

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

Ce code spécifie qu'une date JavaScript doit être récupérée (qui sera la date actuelle) et que le mois, la date du mois et l'année complète doivent être extraits de cette date instanciée. La sortie pour cela apparaît ensuite.

Le dernier exemple fonctionnait sur une chaîne JavaScript arbitraire mais n'utilisait aucun paramètre. L'exemple suivant illustre la fourniture de paramètres à ce traitement de chaîne JavaScript arbitraire, car il démontre l'utilisation de la fonction pow de JavaScript. Le code de cet exemple est indiqué ci-dessous.

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

Le résultat de l'exécution de cet exemple est affiché dans la capture d'écran suivante.

Pour mon dernier exemple de cet article de blog, je démontre la toString()sortie standard du ScriptExceptiondéclaré dans certains des exemples précédents. La ScriptEngine.evalméthode lève cette exception vérifiée s'il y a une erreur lors de l'exécution / évaluation du script fourni. Cette méthode lève également une NullPointerException si la chaîne fournie est null. Le code utilisé pour forcer une erreur de script est indiqué ci-après.

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

Ce code fournit un script absurde (en termes de syntaxe JavaScript), mais c'est exactement ce qui est nécessaire pour démontrer ScriptException.toString (), qui est appelé dans le cadre de la gestion des exceptions dans la méthode indiquée ci-dessus pour gérer une chaîne JavaScript arbitraire . Lorsque le code est exécuté, nous voyons les informations d'exception comme indiqué dans l'image suivante.

La partie de la sortie qui provient ScriptException.toString()est la partie qui indique: "javax.script.ScriptException: sun.org.mozilla.javascript.internal.EvaluatorException: manquant; avant l'instruction (# 1) à la ligne numéro 1."

Le ScriptExceptioncontient le nom de fichier, le numéro de ligne et le numéro de colonne de l'exception, ce qui est particulièrement utile si un fichier avec du code JavaScript est fourni pour l'évaluation.

Conclusion

Java SE 6 simplifie l'utilisation de JavaScript dans le code Java. D'autres moteurs de script peuvent également être associés à Java, mais il est pratique d'en avoir un prêt à l'emploi avec Mozilla Rhino.

Code complet et instantané de l'écran de sortie

Pour être complet, j'inclus la liste complète du code en un seul endroit ici et la sortie résultante après cela.

JavaScriptInJavaExample.java