JavaScript in Java

Il recente post di JavaLobby The Top 10 Unused Features in Java è stato estremamente popolare. Al momento della stesura di questo articolo, è il post in cima alla classifica nella categoria DZone Top Links. Inoltre è stata anche pubblicata una risposta. Ci sono molte osservazioni interessanti sulle funzionalità sottoutilizzate in Java in entrambi i post dei blog e sono d'accordo con alcune più di altre. Tuttavia, l'elemento che ha davvero attirato la mia attenzione è stata l'affermazione che Java SE 6 è una delle funzionalità di Java più inutilizzate.

Mi piace molto lavorare con Java SE 6 e ho scritto o scritto nel blog sulle funzionalità di Java SE 6 diverse volte in passato. In questo post sul blog, intendo dimostrare una parte della capacità di Java SE 6 di ospitare in esecuzione codice JavaScript.

La maggior parte degli sviluppatori Java e degli sviluppatori JavaScript capiscono che oltre alle quattro lettere "JAVA", JavaScript e Java hanno ben poco in comune se non un retaggio di tipo C. Tuttavia, a volte può essere utile eseguire un linguaggio di scripting dall'interno del codice Java e Java SE 6 lo consente.

Il pacchetto javax.script è stato introdotto con Java SE 6 e include classi, interfacce e un'eccezione controllata relativa all'uso dei motori di scripting all'interno di Java. Questo post sul blog si concentrerà su ScriptEngineFactory, ScriptEngineManager, ScriptEngine e ScriptException.

Una delle prime cose che si potrebbe desiderare di fare è determinare quali motori di scripting sono già disponibili. Il prossimo frammento di codice mostra quanto sia facile farlo con 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 + " "); } } 

Il codice mostrato sopra genera un output simile a quello mostrato nell'istantanea della schermata successiva.

Come dimostra questa immagine, il motore JavaScript di Mozilla Rhino è incluso in Java SE 6. di Sun. Vediamo anche alcuni "nomi comuni" associati a questo particolare motore. Uno qualsiasi di questi nomi può essere utilizzato per cercare questo motore. Negli esempi successivi in ​​questo post, userò il nome comune "js" per questa ricerca.

Il prossimo esempio di codice sfrutterà il motore JavaScript di Rhino fornito per eseguire del codice JavaScript dal codice Java. In questo caso, trarremo vantaggio dalla funzione toExponential di 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()); } } 

Il codice precedente richiama direttamente JavaScript utilizzando il metodo ScriptEngine.eval (String) per valutare la stringa fornita contenente la sintassi JavaScript. Prima dell'invocazione del evalmetodo, due parametri vengono "passati" (legati) al codice JavaScript tramite chiamate ScriptEngine.put (String, Object). Si accede all'oggetto risultato del JavaScript eseguito nel codice Java utilizzando una chiamata ScriptEngine.get (String).

Per dimostrare il codice precedente utilizzando la toExponentialfunzione, userò il seguente codice "client".

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

Quando il codice precedente viene eseguito sul metodo writeNumberAsExponential mostrato in precedenza e viene utilizzato JavaScript, l'output appare simile a quello mostrato nell'istantanea della schermata successiva.

Questo esempio è sufficiente per dimostrare quanto sia facile invocare la funzionalità JavaScript da Java SE 6. Tuttavia, ciò potrebbe essere implementato in modo ancora più generico, come dimostreranno i prossimi due esempi. Il primo esempio mostra il richiamo di JavaScript relativamente arbitrario senza parametri passati / associati e il secondo esempio mostra il richiamo di JavaScript relativamente arbitrario con parametri passati / associati.

Una stringa JavaScript relativamente arbitraria può essere elaborata con codice simile a quello mostrato di seguito.

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

Il codice sopra fornisce un po 'di flessibilità in termini di JavaScript che può essere elaborato. Questa probabilmente non è l'idea migliore per il codice di produzione, ma rende più facile dimostrare l'uso di varie funzionalità JavaScript all'interno di Java.

Il primo esempio per utilizzare questa elaborazione JavaScript relativamente arbitraria sfrutta i vantaggi dell'oggetto Date di JavaScript. Il codice di esempio viene mostrato di seguito.

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

Questo codice specifica che deve essere recuperata una data JavaScript (che sarà la data corrente) e che il mese, la data del mese e l'anno intero devono essere estratti da quella data istanziata. L'output per questo viene visualizzato di seguito.

L'ultimo esempio ha funzionato su una stringa JavaScript arbitraria ma non ha utilizzato alcun parametro. Il prossimo esempio mostra la fornitura di parametri a questa elaborazione arbitraria di stringhe JavaScript poiché dimostra l'uso della funzione pow di JavaScript. Il codice per questo esempio è elencato di seguito.

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

L'output dell'esecuzione di questo esempio è mostrato nell'istantanea della schermata seguente.

Per il mio ultimo esempio di questo post sul blog, mostro lo standard toString()output del ScriptExceptiondichiarato in alcuni degli esempi precedenti. Il ScriptEngine.evalmetodo genera questa eccezione controllata se si verifica un errore nell'esecuzione / valutazione dello script fornito. Questo metodo genera anche un'eccezione NullPointerException se la stringa fornita è null. Successivamente viene visualizzato il codice utilizzato per forzare un errore di script.

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

Questo codice fornisce uno script senza senso (in termini di sintassi JavaScript), ma è esattamente ciò che è necessario per dimostrare ScriptException.toString (), che viene chiamato come parte della gestione delle eccezioni nel metodo mostrato sopra per la gestione di una stringa JavaScript arbitraria . Quando il codice viene eseguito, vediamo le informazioni sull'eccezione come mostrato nell'immagine successiva.

La parte dell'output da cui proviene ScriptException.toString()è la parte che afferma: "javax.script.ScriptException: sun.org.mozilla.javascript.internal.EvaluatorException: mancante; prima dell'istruzione (# 1) nella riga numero 1."

Il ScriptExceptioncontiene il nome del file, il numero di riga e il numero di colonna dell'eccezione, che è particolarmente utile se viene fornito un file con codice JavaScript per la valutazione.

Conclusione

Java SE 6 semplifica l'utilizzo di JavaScript all'interno del codice Java. Altri motori di scripting possono anche essere associati a Java, ma è utile averne uno fornito immediatamente con Mozilla Rhino.

Codice completo e istantanea della schermata di output

Per completezza, includo l'elenco completo del codice in un punto qui e l'output risultante dopo.

JavaScriptInJavaExample.java