Донецкий техникум промышленной автоматики

JavaScript EE: Частина 2. Виклик віддалених функцій JavaScript за допомогою Ajax

  1. Серія контенту:
  2. Цей контент є частиною серії: JavaScript EE
  3. Розробка програми моніторингу JVM
  4. Малюнок 1. Приклад програми
  5. Створення Web-сторінки
  6. Лістинг 1. HTML-код MonitorPage.jsp
  7. Лістинг 2. Код JavaScript MonitorPage.jsp
  8. Лістинг 3. Згенеровані функції JavaScript
  9. Лістинг 4. Функція getInfo () MonitorScript.jss
  10. Лістинг 5. Функція allocMem () MonitorScript.jss
  11. Лістинг 6. Функція gc () з MonitorScript.jss
  12. ініціалізація запитів
  13. Лістинг 7. Функції XHR () і newRequest () конструктора xhr.js
  14. Лістинг 8. Функції xhr.js checkRequest (), addParam () і addHeader ()
  15. Лістинг 9. Функція xhr.js sendRequest ()
  16. Лістинг 10. Функція xhr.js isCompleted ()
  17. Лістинг 11. Функція xhr.js showRequestInfo ()
  18. Лістинг 12. Функція xhr.js htmlEncode ()
  19. Авторизація виклику функцій
  20. Лістинг 13. Клас AuthorizedCalls
  21. Лістинг 14. Файл authorize.tag
  22. Застосування тег-файлу для створення коду JavaScript
  23. Лістінг 15. Використання <js: rpc> в MonitorPage.jsp
  24. Лістинг 16. Файл rpc.tag
  25. Лістинг 17. Згенерована функція allocMem ()
  26. Лістинг 18. Згенерована функція getInfo ()
  27. Лістинг 19. Файл init.jss
  28. Лістинг 20. Файл finalize.jss
  29. Висновок
  30. Ресурси для скачування

JavaScript EE

Реалізація RPC для JavaScript із застосуванням коду Ajax і Java

Серія контенту:

Цей контент є частиною # з серії # статей: JavaScript EE

https://www.ibm.com/developerworks/ru/views/global/libraryview.jsp?series_title_by=javascript+ee

Слідкуйте за виходом нових статей цієї серії.

Цей контент є частиною серії: JavaScript EE

Слідкуйте за виходом нових статей цієї серії.

Механізм RPC надзвичайно простий. На сервері є набір функцій JavaScript, і вам треба звертатися до них з Web-браузера, як якщо б у вас був один механізм JavaScript, виконуючий як клієнтський код, так і серверний. Отже, потрібні клієнтські програми, які мали б ті ж імена і параметри, що і їх серверні аналоги.

Ці клієнтські програми будуть через Ajax передавати свої параметри сервера, де відбувається реальна обробка. Сервлет Java буде запускати функції на сервері і повертати результати клієнту з використанням формату JSON. Потім клієнтські програми будуть аналізувати відповіді Ajax і перетворювати рядки JSON назад в об'єкти JavaScript, які повертаються з додатком.

Як розробник програми, ви можете зосередитися на побудові користувальницького інтерфейсу і функцій, які виконуються на сервері. Вам не потрібно вирішувати проблеми Ajax або RPC, тому що ця стаття пропонує генератор коду JavaScript в формі тег-файлу, який ви можете використовувати в своїх JSP-сторінках для автоматичного написання клієнтських програм. Щоб зрозуміти, як це працює, почнемо з прикладу програми.

Розробка програми моніторингу JVM

Приклад програми для цієї статті використовує API Java Management для моніторингу JVM, яка виконує сервер Java ЇЇ, на якому працює додаток. Інтерфейс користувача складається з однієї Web-сторінки, на якій відображаються різні індикатори, такі як число класів Java, споживання ресурсів пам'яті, активність збирача сміття і число потоків.

Ця інформація витягується за допомогою Ajax і заноситься в таблицю HTML. Щоб було цікавіше, Web-сторінка містить форму, яка дозволяє виділити певний обсяг пам'яті на задане число секунд. Крім того, можна активізувати збирач сміття (garbage collector - GC) JVM.

Малюнок 1. Приклад програми

На стороні сервера додаток використовує файл JavaScript, функції якого викликаються з Web-браузера за допомогою Ajax з використанням механізму сценаріїв, який ми розглянули в першій статті цієї серії. Цей простий сервлет буде обробляти всі запити, URL яких закінчується розширенням .jss. Сервлет розшукує на сервері відповідний файл JavaScript і виконує його за допомогою Java Scripting API.

Створення Web-сторінки

У лістингу 1 наведено таблицю і форма файлу MonitorPage.jsp. Кожна клітинка даних забезпечена ідентифікатором, так що зміст таблиці можна редагувати за допомогою JavaScript. Атрибут onload тега <body> використовується для виклику функції JavaScript з ім'ям init (), яка ініціалізує клієнтську частину програми. Дві інші функції з іменами allocMem () і gc () викликаються, коли користувач натискає кнопки.

Лістинг 1. HTML-код MonitorPage.jsp
<Html> <head> ... <style type = "text / css"> th {text-align: left; } Td {text-align: right; } .Space {margin: 10px; } </ Style> </ head> <body onload = "init ()"> <table border = "1" cellpadding = "5"> <tr> <th colspan = 2> Classes </ th> <th colspan = 2> Heap Memory </ th> <th colspan = 2> Non-Heap Memory </ th> <th colspan = 2> Garbage Collector </ th> <th colspan = 2> Threads </ th> </ tr> < tr> <th> Loaded </ th> <td id = "info.classes.loaded"> </ td> <th> Used </ th> <td id = "info.memory.heap.used"> </ td> <th> Used </ th> <td id = "info.memory.nonHeap.used"> </ td> <th> Count </ th> <td id = "info.gc.count"> </ td> <th> Live </ th> <td id = "info.threads.live"> </ td> </ tr> <tr> <th> Unloaded </ th> <td id = "info.classes. unloaded "> </ td> <th> Committed </ th> <td id =" info.memory.heap.committed "> </ td> <th> Committed </ th> <td id =" info.memory. nonHeap.committed "> </ td> <th> Time </ th> <td id =" info.gc.time "> </ td> <th> Peak </ th> <td id =" info.threads. peak "> </ td> </ tr> </ table> <br> <form name =" monitorForm "> Size: <input name =" size "size =" 10 "> <span class =" space "> < / span> Seconds: <input name = "seconds" size = "4"> <span class = "space"> </ span> <button type = "button" onclick = "allocMem (this .form.size.value, this.form.seconds.value) "> Allocate Memory </ button> <span class =" space "> </ span> <button type =" button "onclick =" gc () "> Collect Garbage </ button> </ form> </ body> </ html>

Файл MonitorPage.jsp (див. Лістинг 2) використовує спеціальний тег <js: rpc> для генерації клієнтських функцій JavaScript, які викликають свої серверні аналоги. Тег <js: rpc> має наступні атрибути:

script URL сценарію, який буде виконуватися на сервері function сигнатура дистанційно викликається функції JavaScript validator опциональное вираз, яке обчислюється Web-браузером для перевірки параметрів функції jsonVar ім'я опціональною змінної JavaScript, яка буде зберігати відповідь JSON method метод HTTP, який може приймати значення GET або POST async логічне значення, яке вказує, як повинен використовуватися XMLHttpRequest- асинхронно або синхронно
Лістинг 2. Код JavaScript MonitorPage.jsp
<% @ Taglib prefix = "js" tagdir = "/ WEB-INF / tags / js"%> <html> <head> <title> Monitor </ title> <script src = "xhr.js" type = "text / javascript "> </ script> <script type =" text / javascript "> <js: rpc function =" getInfo () "script =" MonitorScript.jss "method =" GET "async =" true "jsonVar =" json "> showInfo (json," info "); </ Js: rpc> <js: rpc function = "allocMem (size, seconds)" script = "MonitorScript.jss" validator = "valid ( 'Size', size) && valid ( 'Seconds', seconds)" method = "POST" async = "true" /> <js: rpc function = "gc ()" script = "MonitorScript.jss" method = "POST" async = "false"> alert ( "Garbage Collected"); </ Js: rpc> function showInfo (obj, id) {if (typeof obj == "object") {for (var prop in obj) showInfo (obj [prop], id + "." + Prop); } Else {var elem = document.getElementById (id); if (elem) elem.innerHTML = htmlEncode (String (obj)); }} Function valid (name, value) {if (! Value || value == "") {alert (name + "is required"); return false; } Var n = new Number (value); if (isNaN (n) || n <= 0 || Math.floor (n)! = n) {alert (name + "must be a positive integer."); return false; } Else return true; } Function init () {getInfo (); setInterval (getInfo 1000); } </ Script> ... </ head> ... </ html>

Код JavaScript, укладений в теги <js: rpc> і </ js: rpc>, буде використовуватися для обробки відповідей Ajax. Наприклад, в разі функції getInfo () відповідь json передається іншій функції з ім'ям showInfo (), яка рекурсивно переглядає дерево об'єктів, заповнюючи осередку даних HTML-таблиці. Так як async має значення true, згенерувала функція getInfo () повертається відразу після відправлення запиту, і зворотний виклик Ajax активізує функцію showInfo ().

Функція allocMem () використовує атрибут validator для перевірки введення користувача. Якщо для будь-якого з двох параметрів функція valid () повертає значення false, віддалений виклик скасовується і з'являється попереджувальне повідомлення. Функція gc () чекає відповіді, перш ніж повернути управління, тому що async в даному випадку дорівнює false. Функція init () викликає getInfo (), щоб ініціалізувати призначений для користувача інтерфейс, і передає ту ж функцію setInterval (), так що та викликається щосекунди для оновлення інформації Web-сторінки.

У лістингу 3 міститься код, отриманий за допомогою спеціального тега <js: rpc>, який реалізований як тег-файл з ім'ям rpc.tag. Кожна згенерувала функція використовує об'єкт XHR, прототип якого можна знайти в файлі xhr.js, який MonitorPage.jsp імпортує в свій заголовок. Вихідний код обох файлів rpc.tag і xhr.js буде представлений нижче в цій статті.

Лістинг 3. Згенеровані функції JavaScript
var getInfoXHR = new XHR ( "GET", "MonitorScript.jss", true); function getInfo () {var request = getInfoXHR.newRequest (); getInfoXHR.addHeader ( "Ajax-Call", "getInfo ()"); function processResponse () {if (getInfoXHR.isCompleted ()) {var json = eval (request.responseText); showInfo (json, "info"); }} GetInfoXHR.sendRequest (processResponse); } Var allocMemXHR = new XHR ( "POST", "MonitorScript.jss", true); function allocMem (size, seconds) {if (! (valid ( 'Size', size) && valid ( 'Seconds', seconds))) return; var request = allocMemXHR.newRequest (); allocMemXHR.addHeader ( "Ajax-Call", "allocMem (size, seconds)"); allocMemXHR.addParam ( "size", size); allocMemXHR.addParam ( "seconds", seconds); function processResponse () {if (allocMemXHR.isCompleted ()) {}} allocMemXHR.sendRequest (processResponse); } Var gcXHR = new XHR ( "POST", "MonitorScript.jss", false); function gc () {var request = gcXHR.newRequest (); gcXHR.addHeader ( "Ajax-Call", "gc ()"); function processResponse () {if (gcXHR.isCompleted ()) {alert ( "Garbage Collected"); }} GcXHR.sendRequest (processResponse); }

Складання сценарію для сервера

Файл MonitorScript.jss містить три функції JavaScript, які виконуються на сервері. Функція getInfo () (показана в лістингу 4) через Java Management API отримує інформацію JVM щодо класів, пам'яті і потоків. Всі дані упаковані в дерево об'єктів, яке повертається як JSON у відповідь на запит Ajax. Функція getInfo () не робить нічого особливого. У наступних розділах ви побачите, як реалізований механізм RPC.

Лістинг 4. Функція getInfo () MonitorScript.jss
importClass (java.lang.management.ManagementFactory); function getInfo () {var classes = ManagementFactory.getClassLoadingMXBean (); var memory = ManagementFactory.getMemoryMXBean (); var heap = memory.getHeapMemoryUsage (); var nonHeap = memory.getNonHeapMemoryUsage (); var gc = ManagementFactory.getGarbageCollectorMXBeans (); var threads = ManagementFactory.getThreadMXBean (); var gcCount = 0; var gcTime = 0; for (var i = 0; i <gc.size (); i ++) {gcCount + = gc.get (i) .collectionCount; gcTime + = gc.get (i) .collectionTime; } Return {classes: {loaded: classes.loadedClassCount, unloaded: classes.unloadedClassCount,}, memory: {heap: {init: heap.init, used: heap.used, committed: heap.committed, max: heap.max} , nonHeap: {init: nonHeap.init, used: nonHeap.used, committed: nonHeap.committed, max: nonHeap.max}}, gc: {count: gcCount, time: gcTime}, threads: {live: threads.threadCount , peak: threads.peakThreadCount}}; }

Функція allocMem () (див. Лістинг 5) створює потік Java, який виконує метод run (), реалізований в JavaScript. Програма створює масив byte, використовуючи метод newInstance () класу java.lang.reflect.Array, і викликає java.lang.Thread.sleep (). Після цього масив готовий до збору сміття.

Лістинг 5. Функція allocMem () MonitorScript.jss
function allocMem (size, seconds) {var runnable = new java.lang.Runnable () {run: function () {var array = new java.lang.reflect.Array.newInstance (java.lang.Byte.TYPE, size) ; java.lang.Thread.sleep (seconds * 1000); }} Var thread = new java.lang.Thread (runnable); thread.start (); }

У лістингу 6 показана функція gc (), яка активізує збирач сміття JVM.

Лістинг 6. Функція gc () з MonitorScript.jss
function gc () {java.lang.System.gc (); }

Побудова пакувальника XMLHttpRequest

У попередньому розділі ми розглянули код JavaScript, створений тегом <js: rpc>. Щоб мінімізувати і спростити згенерований код, всі операції, пов'язані з API XMLHttpRequest, такі як створення нового об'єкта запитів або відправка запиту HTTP, поміщаються в окремий файл JavaScript з ім'ям xhr.js. У цьому розділі представлені методи об'єкта XHR.

ініціалізація запитів

Конструктор XHR () (див. Лістинг 7) бере три параметра (method, url і async) і зберігає їх як властивості. Метод newRequest () перериває попередній запит, якщо він все ще активний, звільняє пам'ять за допомогою оператора delete і створює новий екземпляр XMLHttpRequest. Така поведінка підходить для додатків, що використовують Ajax для зв'язку з каналом передачі даних або для збереження введення користувача. У типовому випадку інтерес представляє завантаження або збереження тільки самих останніх даних.

Лістинг 7. Функції XHR () і newRequest () конструктора xhr.js
function XHR (method, url, async) {this.method = method.toUpperCase (); this.url = url; this.async = async; } XHR.prototype.newRequest = function () {var request = this.request; if (request) {request.onreadystatechange = function () {}; if (request.readyState! = 4) request.abort (); delete request; } Request = null; if (window.ActiveXObject) request = new ActiveXObject ( "Microsoft.XMLHTTP"); else if (window.XMLHttpRequest) request = new XMLHttpRequest (); this.request = request; this.sent = false; this.params = new Array (); this.headers = new Array (); return request; }

У лістингу 8 показані методи addParam () і addHeader (), які дозволяють додати параметри запиту та заголовки, включені в запит HTTP при його відправці. Ці методи можна використовувати відразу, як тільки створений новий запит. Функція checkRequest () видасть попередження, якщо об'єкт XMLHttpRequest ні створено або якщо запит вже був відправлений.

Лістинг 8. Функції xhr.js checkRequest (), addParam () і addHeader ()
XHR.prototype.checkRequest = function () {if (! This.request) throw "Request not created"; if (this.sent) throw "Request already sent"; return true; } XHR.prototype.addParam = function (pname, pvalue) {this.checkRequest (); this.params [this.params.length] = {name: pname, value: pvalue}; } XHR.prototype.addHeader = function (hname, hvalue) {this.checkRequest (); this.headers [this.headers.length] = {name: hname, value: hvalue}; }

Відправлення запитів

Функція sendRequest () (див. Лістинг 9) кодує параметри, відкриває запит, додає заголовки, встановлює зворотний виклик Ajax, якщо async дорівнює true, а потім відправляє запит HTTP. Якщо async дорівнює false, зворотний виклик активізується після send ().

Лістинг 9. Функція xhr.js sendRequest ()
XHR.prototype.sendRequest = function (callback) {this.checkRequest (); var query = ""; for (var i = 0; i <this.params.length; i ++) {if (query.length> 0) query + = "&"; query + = encodeURIComponent (this.params [i] .name) + "=" + encodeURIComponent (this.params [i] .value); } Var url = this.url; if (this.method == "GET" && query.length> 0) {if (url.indexOf ( "?") == -1) url + = "?"; else url + = "&"; url + = query; } This.request.open (this.method, url, this.async); for (var i = 0; i <this.headers.length; i ++) this.request.setRequestHeader (this.headers [i] .name, this.headers [i] .value); if (this.async) this.request.onreadystatechange = callback; var body = null; if (this.method == "POST") {body = query; this.request.setRequestHeader ( "Content-Type", "application / x-www-form-urlencoded"); } This.sent = true; this.request.send (body); if (! this.async) callback (); }

У зворотному виклику Ajax можна використовувати isCompleted () (див. Лістинг 10) для перевірки статусу запиту.

Лістинг 10. Функція xhr.js isCompleted ()
XHR.prototype.isCompleted = function () {if (this.request && this.sent) if (this.request.readyState == 4) if (this.request.status == 200) return true; else this.showRequestInfo (); return false; }

Повідомлення про помилки

У разі помилки на стороні сервера isCompleted () викликає функцію showRequestInfo () (див. Лістинг 11). Ця функція відкриває вікно і роздруковує інформацію запиту: метод HTTP, URL, параметри, заголовки і відповідь.

Лістинг 11. Функція xhr.js showRequestInfo ()
var xhrErrorWindow = null; XHR.prototype.showRequestInfo = function () {if (xhrErrorWindow && (xhrErrorWindow.closed || xhrErrorWindow._freeze)) return; xhrErrorWindow = window.open ( "", "XHRError", "menubar = no, resizable = yes," + "scrollbars = yes, width = 600, height = 600"); var doc = xhrErrorWindow.document; doc.writeln ( "<p align = 'right'>"); doc.writeln ( "<button onclick = 'window._freeze = true'> Freeze </ button>") doc.writeln ( "</ p>"); doc.writeln (htmlEncode (this.method + "" + this.url)); doc.writeln ( "<pre>" + this.request.status + "</ pre>"); doc.writeln ( "Parameters:"); doc.writeln ( "<pre>"); for (var i = 0; i <this.params.length; i ++) {doc.writeln (htmlEncode (this.params [i] .name + "=" + this.params [i] .value)); } Doc.writeln ( "</ pre>"); doc.writeln ( "Headers:"); doc.writeln ( "<pre>"); for (var i = 0; i <this.headers.length; i ++) {doc.writeln (htmlEncode (this.headers [i] .name + "=" + this.headers [i] .value)); } Doc.writeln ( "</ pre>"); doc.writeln ( "Response:"); var response = this.request.responseText; doc.writeln (response); doc.close (); xhrErrorWindow.focus (); }
Дозвіл window.focus () в Firefox

Виберіть Tools> Options> Content, натисніть кнопку Advanced поруч з Enable JavaScript і встановіть прапорець Rise or lower windows на вкладці Advanced JavaScript Settings.

Якщо попередня помилка HTTP вже відкрила вікно повідомлень про помилки, виклик focus () робить його поточним вікном, що може викликати проблеми, якщо помилка HTTP повторюється знову і знову. До того ж в цьому випадку помилку важко проаналізувати, тому що зміст вікна оновлюється кожну секунду, що робить неможливою прокрутку.

Щоб вирішити ці проблеми, функція showRequestInfo () додає кнопку, яка встановлює змінну з ім'ям _freeze. Якщо значення _freeze одно true, інформація запитів не оновлюється. Крім того, вікно помилок більше не відкривається, якщо користувач його закрив. Після внесення змін до коду можна просто оновити сторінку додатка, щоб перевірити, чи залишилася помилка або виправилася.

Функція htmlEncode () (див. Лістинг 12) бере параметр рядка і замінює символи &, <і> відповідно символами & amp ;, & lt; і & gt ;.

Лістинг 12. Функція xhr.js htmlEncode ()
function htmlEncode (value) {return value? value.replace (/ & / g, "& amp;") .replace (/ </ g, "& lt;"). replace (/> / g, "& gt;"): ""; }

Реалізація механізму JavaScript-RPC

У цьому розділі представлений тег-файл JSP, який генерує функції JavaScript, що реалізують клієнтську частину механізму RPC. Ви побачите також, як активізуються їх серверні аналоги і як повертаються результати. Однак спочатку кілька зауважень з безпеки.

Авторизація виклику функцій

Сценарії на сервері можна розглядати як звичайні ресурси, і доступ до них можна обмежити із застосуванням стандартних процедур безпеки Java ЇЇ. Зазвичай визначають одну або кілька ролей, наділених правами доступу до сценаріїв в залежності від обмежень безпеки, прописаних в файлі web.xml.

Обмежуєте ви доступ до своїх сценаріями чи ні, Web-клієнти не повинні мати можливість активізувати будь-які функції сценарію на сервері. Простий спосіб управління тим, які функції можуть викликатися через механізм RPC, полягає в тому, щоб зібрати їх в колекцію Set. Компонент AuthorizedCalls (див. Лістинг 13) надає безпечні методи керування набором авторизованих викликів.

Лістинг 13. Клас AuthorizedCalls
package jsee.rpc; import java.util. *; public class AuthorizedCalls implements java.io.Serializable {private Set <String> calls; public AuthorizedCalls () {calls = new HashSet <String> (); } Protected String encode (String scriptURI, String functionName) {return scriptURI + '#' + functionName; } Public synchronized void add (String scriptURI, String functionName) {calls.add (encode (scriptURI, functionName)); } Public synchronized void remove (String scriptURI, String functionName) {calls.remove (encode (scriptURI, functionName)); } Public synchronized boolean contains (String scriptURI, String functionName) {return calls.contains (encode (scriptURI, functionName)); } Public String toString () {return calls.toString (); }}

Приклад програми для цієї статті повинен авторизувати виклики зі сторінки JSP. Файл authorize.tag (див. Лістинг 14) має два атрибути (з іменами function і script), значення яких передаються методу add () компонента AuthorizedCalls. Крім того, відносний URI будь-якого сценарію перетворюється в абсолютний URI, щоб гарантувати унікальну ідентифікацію кожного сценарію відповідним URI. Так як екземпляр AuthorizedCalls зберігається в області session, функції на стороні сервера будуть виконуватися тільки за дорученням авторизованих користувачів (в тому випадку, якщо ви обмежили доступ до сценарія для деяких з своїх користувачів).

Лістинг 14. Файл authorize.tag
<% @ Attribute name = "function" required = "true" rtexprvalue = "true"%> <% @ attribute name = "script" required = "true" rtexprvalue = "true"%> <jsp: useBean id = "authorizedCalls "class =" jsee.rpc.AuthorizedCalls "scope =" session "/> <% String functionName = (String) jspContext.getAttribute (" function "); String scriptURI = (String) jspContext.getAttribute ( "script"); if (! scriptURI.startsWith ( "/")) {String base = request.getRequestURI (); base = base.substring (0, base.lastIndexOf ( "/")); scriptURI = base + "/" + scriptURI; } AuthorizedCalls.add (scriptURI, functionName); %>

Інший пов'язаний з безпекою аспект, який дуже важливо проаналізувати, - це те, як параметри дистанційно викликається функції обробляються на стороні сервера. Можна було б спробувати зашифрувати об'єкти JavaScript як рядки JSON в Web-браузері і відправити їх на сервер, де їх легко розшифрувати за допомогою eval (). Але це було б великою помилкою; це дозволило б зловмисникам підкидати код, який буде виконуватися на вашому сервері.

Приклад коду для цієї статті допускає в якості параметрів, що передаються за допомогою Ajax, тільки примітивні типи (такі як рядки і числа). На стороні сервера вони розглядаються як рядки, що дозволяє механізму JavaScript при необхідності автоматично перетворювати їх в числа. Якщо потрібні більш складні типи, не слід спиратися на eval () для розшифровки параметрів на сервері. Використовуйте свої власні методи шифрування / дешифрування.

Застосування тег-файлу для створення коду JavaScript

Файл MonitorPage.jsp, наведений в першій частині цієї статті, використовує тег <js: rpc> (див. Лістинг 15) для генерування підпрограм JavaScript, які викликають функції на стороні сервера.

Лістінг 15. Використання <js: rpc> в MonitorPage.jsp
<Js: rpc function = "getInfo ()" script = "MonitorScript.jss" method = "GET" async = "true" jsonVar = "json"> showInfo (json, "info"); </ Js: rpc> <js: rpc function = "allocMem (size, seconds)" script = "MonitorScript.jss" validator = "valid ( 'Size', size) && valid ( 'Seconds', seconds)" method = "POST" async = "true" /> <js: rpc function = "gc ()" script = "MonitorScript.jss" method = "POST" async = "false"> alert ( "Garbage Collected"); </ Js: rpc>

У лістінгу 16 наведено файл rpc.tag, Який реалізує цею Спеціальний тег. Тег-файл декларує свої атрибути і використовувані бібліотеки JSP, активізує файл authorize.tag за допомогою <js: authorize>, встановлює дві змінні JSP з іменами xhrVar і paramList і генерує функцію JavaScript на стороні клієнта з заданим ім'ям і параметрами.

Мінлива xhrVar використовується на стороні сервера для визначення імені змінної JavaScript, що використовується в створеному коді JavaScript, який буде виконуватися в Web-браузері. Значення змінної xhrVar складається з імені функції і рядки XHR. Наприклад, якщо function це getInfo (), то значення змінної JSP (і ім'я змінної JavaScript) буде getInfoXHR.

Інша змінна JSP з ім'ям paramList зберігає список параметрів, які передаються через атрибут function між (і). Наприклад, якщо function - це allocMem (size, seconds), то змінна paramList буде зберігати список size, seconds.

Лістинг 16. Файл rpc.tag
<% @ Attribute name = "function" required = "true" rtexprvalue = "true"%> <% @ attribute name = "script" required = "true" rtexprvalue = "true"%> <% @ attribute name = "validator "required =" false "rtexprvalue =" true "%> <% @ attribute name =" jsonVar "required =" false "rtexprvalue =" true "%> <% @ attribute name =" method "required =" false "rtexprvalue = "true"%> <% @ attribute name = "async" required = "true" rtexprvalue = "true" type = "java.lang.Boolean"%> <% @ taglib prefix = "c" uri = "http: / /java.sun.com/jsp/jstl/core "%> <% @ taglib prefix =" fn "uri =" http://java.sun.com/jsp/jstl/functions "%> <% @ taglib prefix = "js" tagdir = "/ WEB-INF / tags / js"%> <js: authorize script = "$ {script}" function = "$ {function}" /> <c: set var = "xhrVar" value = "$ {fn: trim (fn: substringBefore (function, '('))} XHR" /> <c: set var = "paramList" value = "$ {fn: substringBefore (fn: substringAfter (function, '( '),') ')} "/> var $ {xhrVar} = new XHR (" $ {method} "," $ {script} ", $ {async}); function $ {function} {<c: if test = "$ {! empty validator}"> if (! ($ {validator})) return; </ c: i f> var request = $ {xhrVar} .newRequest (); $ {XhrVar} .addHeader ( "Ajax-Call", "$ {function}"); <C: forEach var = "paramName" items = "$ {paramList}"> <c: set var = "paramName" value = "$ {fn: trim (paramName)}" /> $ {xhrVar} .addParam ( " $ {paramName} ", $ {paramName}); </ C: forEach> function processResponse () {if ($ {xhrVar} .isCompleted ()) {<c: if test = "$ {! Empty jsonVar}"> var $ {jsonVar} = eval (request.responseText) ; </ C: if> <jsp: doBody />}} $ {xhrVar} .sendRequest (processResponse); }

Перший рядок коду JavaScript, згенерувала rpc.tag, створює об'єкт XHR. Потім тег-файл створює функцію JavaScript, яка може використовуватися в Web-браузері для активізації своєї аналога на сервері. Якщо атрибут validator має непорожнє значення, вираз JavaScript включається в згенерований код, щоб вирішити, чи можна виконати дистанційний виклик.

Потім функцією newRequest () инициализируется новий об'єкт XMLHttpRequest, який зберігається в локальній змінній JavaScript з ім'ям request. Згенерований код додасть заголовок Ajax-Call, значення якого є сигнатурою функції. Після цього додаються параметри в об'єкт XHR. У лістингу 17 наведено код, що згенерував для функції allocMem ().

Лістинг 17. Згенерована функція allocMem ()
var allocMemXHR = new XHR ( "POST", "MonitorScript.jss", true); function allocMem (size, seconds) {if (! (valid ( 'Size', size) && valid ( 'Seconds', seconds))) return; var request = allocMemXHR.newRequest (); allocMemXHR.addHeader ( "Ajax-Call", "allocMem (size, seconds)"); allocMemXHR.addParam ( "size", size); allocMemXHR.addParam ( "seconds", seconds); function processResponse () {if (allocMemXHR.isCompleted ()) {}} allocMemXHR.sendRequest (processResponse); }

Після ініціалізації об'єкта XHR файл rpc.tag генерує зворотний виклик Ajax з ім'ям processResponse (). Ця функція перевіряє, скомпільовано чи запит Ajax, і аналізує відповідь, якщо присутній атрибут jsonVar.

Те, що розташоване на сторінці JSP між <js: rpc> і </ js: rpc>, включається в зворотний виклик Ajax за допомогою <jsp: doBody />. Наприклад, елемент MonitorPage <js: rpc function = "getInfo ()" ...>. Jsp містить showInfo (json, "info"); для обробки відповіді JSON. У лістингу 18 показано, де цей код розміщується всередині функції getInfo (), що генерується за допомогою rpc.tag.

Лістинг 18. Згенерована функція getInfo ()
var getInfoXHR = new XHR ( "GET", "MonitorScript.jss", true); function getInfo () {var request = getInfoXHR.newRequest (); getInfoXHR.addHeader ( "Ajax-Call", "getInfo ()"); function processResponse () {if (getInfoXHR.isCompleted ()) {var json = eval (request.responseText); showInfo (json, "info"); }} GetInfoXHR.sendRequest (processResponse); }

Активізація функції сценарію

Кожен раз, коли в Web-браузері викликається згенерувала функція, через об'єкт XHR відправляється запит Ajax, URL якого закінчується на .jss. На додаток до цього в якості заголовка HTTP з ім'ям Ajax-Call передається сигнатура функції, яка повинна активізуватися. Запитами .jss управляє сервлет з ім'ям JSServlet, який описаний в першій частині цієї серії.

Коли з прикладу додатки запитується MonitorScript.jss, насправді JSServlet виконує три сценарії: init.jss, MonitorScript.jss і finalize.jss. Сценарій init.jss (див. Лістинг 19) отримує параметри запиту, які представляють собою рядки Java, перетворюючи їх в рядки JavaScript і зберігаючи параметри як властивості об'єкта param. Сценарій init.jss також надає функції для отримання і установки bean-компонентів Java.

Лістинг 19. Файл init.jss
var debug = true; var debugStartTime = java.lang.System.nanoTime (); var param = new Object (); var paramValues ​​= new Object (); function initParams () {var paramNames = request.getParameterNames (); while (paramNames.hasMoreElements ()) {var name = paramNames.nextElement (); param [name] = String (request.getParameter (name)); paramValues ​​[name] = new Array (); var values ​​= request.getParameterValues ​​(name); for (var i = 0; i <values.length; i ++) paramValues ​​[name] [i] = String (values ​​[i]); }} InitParams (); function getBean (scope, id) {return eval (scope) .getAttribute (id); } Function setBean (scope, id, bean) {if (! Bean) bean = eval (id); return eval (scope) .setAttribute (id, bean); }

Так як всі три сценарії виконуються в одному і тому ж контексті, finalize.jss (показаний в лістингу 20) може використовувати змінні і функції init.jss і MonitorScript.jss. Сценарій finalize.jss script отримує заголовок Ajax-Call, перевіряє, авторизований чи виклик, через eval () активізує функцію сценарію і перетворює повернутий об'єкт в рядок JSON із застосуванням toSource (). Оскільки параметри функції передаються як параметри запиту, їх значення беруться з об'єкта param.

Лістинг 20. Файл finalize.jss
var ajaxCall = request.getHeader ( "Ajax-Call"); if (ajaxCall! = null) {var authorizedCalls = getBean ( "session", "authorizedCalls"); if (authorizedCalls.contains (request.requestURI, ajaxCall)) {var ajaxResponse = eval ( "with (param)" + ajaxCall); if (ajaxResponse) print (ajaxResponse.toSource ()); }} Var debugEndTime = java.lang.System.nanoTime (); if (debug) println ( "// Time:" + (debugEndTime - debugStartTime) + "ns");

Використовувати eval () для виконання функції безпечно, так як заголовок Ajax-Call перевірений за допомогою authorizedCalls.contains ().

Висновок

З цієї статті ви дізналися, як застосовувати RPC в додатках Ajax і Java, які використовують код JavaScript на стороні серверів і клієнтів. Ви побачили також, як реалізувати інтерфейси Java за допомогою JavaScript, як створювати масиви Java і запускати потоки в своєму коді JavaScript і як управляти життєвим циклом запитів Ajax при приєднанні до каналів передачі даних.

Ресурси для скачування

Схожі тими

  • оригінал статті (EN)
  • у статті JavaScript EE, частина 1: Виконання файлів JavaScript на стороні сервера показано, як компілювати і виконувати файли JavaScript за допомогою API javax.script, як експортувати об'єкти Java у вигляді змінних сценарію і як створювати файли JavaScript, які виконуються на стороні сервера. (EN)
  • Ajax і Java development made simpler, Part 1 (DeveloperWorks, квітень 2008 року) - ще одна стаття, в якій розглядається ідея динамічного генерування коду JavaScript за допомогою тег-файлів JSP (EN).
  • Центр ресурсів по Ajax developerWorks містить постійно поповнюється бібліотеку матеріалів по Ajax, а також корисні ресурси для тих, хто тільки приступає до розробки Ajax-додатків. (EN)

Підпішіть мене на ПОВІДОМЛЕННЯ до коментарів

Jsp?
Url; if (this.method == "GET" && query.length> 0) {if (url.indexOf ( "?") == -1) url + = "?