JavaScript в Java

Последният пост на JavaLobby Топ 10 на неизползваните функции в Java беше изключително популярен. По време на това писане това е най-високо класираната публикация в категорията DZone Top Links. В допълнение е публикуван и отговор на него. Има много интересни наблюдения за недостатъчно използваните функции в Java в двата блога и аз съм съгласен с някои повече от други. Елемент, който наистина привлече вниманието ми, беше твърдението, че Java SE 6 е една от най-неизползваните функции на Java.

Наистина ми харесва да работя с Java SE 6 и съм писал или писал в блогове за функции на Java SE 6 няколко пъти в миналото. В това публикуване в блога възнамерявам да демонстрирам част от способността на Java SE 6 да хоства изпълнение на JavaScript код.

Повечето разработчици на Java и разработчици на JavaScript разбират, че освен четирите букви „JAVA“, JavaScript и Java имат много малко общо, освен някои подобни на C наследство. И все пак може да бъде полезно понякога да стартирате скриптов език от Java кода и Java SE 6 позволява това.

Пакетът javax.script е въведен с Java SE 6 и включва класове, интерфейси и отметнато изключение, свързано с използването на скриптови машини в Java. Това публикуване в блога ще се фокусира върху ScriptEngineFactory, ScriptEngineManager, ScriptEngine и ScriptException.

Едно от първите неща, които може да искате да направите, е да определите кои скриптове са вече налични. Следващият фрагмент от код показва колко лесно е това да се направи с 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 + " "); } } 

Показаният по-горе код генерира изход като този, показан в следващата екранна снимка.

Както показва това изображение, двигателят Mozilla Rhino JavaScript е включен в Java SE 6. От Sun виждаме и някои „общи имена“, които са свързани с този конкретен двигател. Всяко от тези имена може да се използва за търсене на този двигател. В следващите примери в тази публикация ще използвам общото име "js" за това търсене.

Следващият пример за код ще се възползва от предоставения Rhino JavaScript двигател, за да изпълни някои JavaScript кодове от Java код. В този случай ще се възползваме от функцията toExponential на 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()); } } 

Кодът по-горе директно извиква JavaScript, използвайки метода ScriptEngine.eval (String), за да оцени предоставения низ, съдържащ синтаксиса на JavaScript. Преди извикване на evalметода два параметъра се "предават" (обвързват) към JavaScript кода чрез извиквания ScriptEngine.put (String, Object). Резултатният обект на изпълнения JavaScript е достъпен в кода на Java с помощта на извикване ScriptEngine.get (String).

За да демонстрирам горния код с помощта на 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, изходът изглежда подобен на този, показан в следващата екранна снимка.

Този пример е достатъчен, за да демонстрира колко лесно е да се извика функционалността на JavaScript от Java SE 6. Това обаче може да бъде приложено дори по-общо, както ще бъдат показани следващите два примера. Първият пример показва извикване на относително произволен JavaScript без параметри, предадени / обвързани, а вторият пример показва извикване на относително произволен 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, който може да бъде обработен. Това може би не е най-добрата идея за производствен код, но улеснява демонстрирането на използването на различни функции на JavaScript в Java.

Първият пример за използване на тази относително произволна обработка на JavaScript се възползва от обекта Date на JavaScript. Следва примерният код.

 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 String, тъй като демонстрира използването на функцията 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метода хвърля тази проверява изключение, ако има грешка при изпълнение / оценка на предоставената сценария. Този метод също изхвърля NullPointerException, ако предоставеният String е null. Кодът, използван за налагане на грешка в скрипта е показан по-нататък.

 /** * 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), но точно това е необходимо, за да се демонстрира ScriptException.toString (), който се извиква като част от обработката на изключения в метода, показан по-горе за обработка на произволен JavaScript низ . Когато кодът се изпълни, виждаме информацията за изключение, както е показано на следващото изображение.

Частта от изхода, която идва от ScriptException.toString()е частта, която гласи: "javax.script.ScriptException: sun.org.mozilla.javascript.internal.EvaluatorException: липсва; преди оператор (# 1) в ред номер 1."

В ScriptExceptionсъдържа името на файла, номера на реда и колоната броя на изключението, което е особено полезно, ако се предоставя на файл с код JavaScript за оценка.

Заключение

Java SE 6 улеснява използването на JavaScript в Java кода. Други Java скриптове също могат да бъдат свързани с Java, но е удобно да имате такъв, който е доставен с Mozilla Rhino.

Пълна снимка на кода и изходния екран

За пълнота тук включвам на едно място пълния списък с кодове и резултантния изход след това.

JavaScriptInJavaExample.java